diff options
26 files changed, 427 insertions, 288 deletions
@@ -1,6 +1,8 @@ CHANGELOG Roundcube Webmail =========================== +- Support messages import from zip archives +- Zipdownload: Added mbox format support (#1486069) - Drop support for IE6, move IE7/IE8 support to legacy_browser plugin - Update to jQuery-2.1.0 - Search across multiple folders (#1485234) @@ -10,12 +12,16 @@ CHANGELOG Roundcube Webmail - Set In-Reply-To and References for forwarded messages (#1489593) - Removed redundant default_folders config option (#1489737) - Implemented IMAP SPECIAL-USE extension support [RFC6154] (#1487830) +- Fix mbox files import - Fix bug where "With attachment" option in search filter wasn't selected after return from mail view (#1489774) - Fix "washing" of unicoded style attributes (#1489777) - Fix unintentional redirect from compose page in Webkit browsers (#1489789) - Fix messages index cache update under some conditions (e.g. proxy) (#1489756) - Fix lack of translation of special folders in some configurations (#1489799) - Fix XSS issue in plain text spellchecker (#1489806) +- Fix invalid page title for some folders (1489804) +- Fix redundant alert message on over-size uploads (#1489817) +- Fix next message display after removing a message (#1489800) RELEASE 1.0.0 ------------- diff --git a/config/defaults.inc.php b/config/defaults.inc.php index 5c5fccb1e..7f65b9748 100644 --- a/config/defaults.inc.php +++ b/config/defaults.inc.php @@ -56,7 +56,7 @@ $config['db_table_dsn'] = array( // LOGGING/DEBUGGING // ---------------------------------- -// system error reporting, sum of: 1 = log; 4 = show, 8 = trace +// system error reporting, sum of: 1 = log; 4 = show $config['debug_level'] = 1; // log driver: 'syslog' or 'file'. diff --git a/installer/config.php b/installer/config.php index fd7932af4..8b98dbef6 100644 --- a/installer/config.php +++ b/installer/config.php @@ -198,9 +198,6 @@ echo '<label for="cfgdebug1">Log errors</label><br />'; echo $check_debug->show(($value & 4) ? 4 : 0, array('value' => 4, 'id' => 'cfgdebug4')); echo '<label for="cfgdebug4">Print errors (to the browser)</label><br />'; -echo $check_debug->show(($value & 8) ? 8 : 0, array('value' => 8, 'id' => 'cfgdebug8')); -echo '<label for="cfgdebug8">Verbose display (enables debug console)</label><br />'; - ?> </dd> diff --git a/plugins/legacy_browser/js/iehacks.js b/plugins/legacy_browser/js/iehacks.js new file mode 100644 index 000000000..129ad6003 --- /dev/null +++ b/plugins/legacy_browser/js/iehacks.js @@ -0,0 +1,20 @@ + +// Make getElementById() case-sensitive on IE7 +document._getElementById = document.getElementById; +document.getElementById = function(id) { + var i = 0, obj = document._getElementById(id); + + if (obj && obj.id != id) + while ((obj = document.all[i]) && obj.id != id) + i++; + + return obj; +} + +// fix missing :last-child selectors +$(document).ready(function() { + if (rcmail && rcmail.env.skin != 'classic') + $('ul.treelist ul').each(function(i, ul) { + $('li:last-child', ul).css('border-bottom', 0); + }); +}); diff --git a/plugins/legacy_browser/legacy_browser.php b/plugins/legacy_browser/legacy_browser.php index c910d76d4..bdf831e73 100644 --- a/plugins/legacy_browser/legacy_browser.php +++ b/plugins/legacy_browser/legacy_browser.php @@ -23,10 +23,12 @@ class legacy_browser extends rcube_plugin function send_page($args) { // replace jQuery 2.x with 1.x - $ts = filemtime($this->home . '/js/jquery.min.js'); + $ts1 = filemtime($this->home . '/js/jquery.min.js'); + $ts2 = filemtime($this->home . '/js/iehacks.js'); $args['content'] = preg_replace( - '|"program/js/jquery\.min\.js\?s=[0-9]+"|', - '"plugins/legacy_browser/js/jquery.min.js?s=' . $ts . '"', + '|<script src="program/js/jquery\.min\.js\?s=[0-9]+" type="text/javascript"></script>|', + '<script src="plugins/legacy_browser/js/jquery.min.js?s=' . $ts1 . '" type="text/javascript"></script>'."\n" + .'<script src="plugins/legacy_browser/js/iehacks.js?s=' . $ts2 . '" type="text/javascript"></script>', $args['content'], 1); return $args; @@ -53,17 +55,6 @@ class legacy_browser extends rcube_plugin '<link rel="stylesheet" type="text/css" href="plugins/legacy_browser/larry/iehacks.css" />' ); } - - // fix missing :last-child selectors - $rcube->output->add_footer(implode("\n", array( - '<script type="text/javascript">', - '$(document).ready(function() {', - ' $(\'ul.treelist ul\').each(function(i,ul) {', - ' $(\'li:last-child\', ul).css(\'border-bottom\', 0);', - ' });', - '});', - '</script>' - ))); } } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index a1bcc86ca..4b57f760d 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -352,14 +352,13 @@ class rcube_sieve_engine header("Content-Type: application/octet-stream"); header("Content-Length: ".strlen($script)); - if ($browser->ie) + if ($browser->ie) { header("Content-Type: application/force-download"); - if ($browser->ie && $browser->ver < 7) - $filename = rawurlencode(abbreviate_string($script_name, 55)); - else if ($browser->ie) $filename = rawurlencode($script_name); - else + } + else { $filename = addcslashes($script_name, '\\"'); + } header("Content-Disposition: attachment; filename=\"$filename.txt\""); echo $script; diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js index 46ebc92de..22d57e159 100644 --- a/plugins/managesieve/managesieve.js +++ b/plugins/managesieve/managesieve.js @@ -72,22 +72,25 @@ if (window.rcmail) { if (rcmail.gui_objects.filterslist) { rcmail.filters_list = new rcube_list_widget(rcmail.gui_objects.filterslist, {multiselect:false, draggable:true, keyboard:false}); - rcmail.filters_list.addEventListener('select', function(e) { p.managesieve_select(e); }); - rcmail.filters_list.addEventListener('dragstart', function(e) { p.managesieve_dragstart(e); }); - rcmail.filters_list.addEventListener('dragend', function(e) { p.managesieve_dragend(e); }); - rcmail.filters_list.row_init = function (row) { - row.obj.onmouseover = function() { p.managesieve_focus_filter(row); }; - row.obj.onmouseout = function() { p.managesieve_unfocus_filter(row); }; - }; - rcmail.filters_list.init(); - rcmail.filters_list.focus(); + + rcmail.filters_list + .addEventListener('select', function(e) { p.managesieve_select(e); }) + .addEventListener('dragstart', function(e) { p.managesieve_dragstart(e); }) + .addEventListener('dragend', function(e) { p.managesieve_dragend(e); }) + .addEventListener('initrow', function(row) { + row.obj.onmouseover = function() { p.managesieve_focus_filter(row); }; + row.obj.onmouseout = function() { p.managesieve_unfocus_filter(row); }; + }) + .init().focus(); } if (rcmail.gui_objects.filtersetslist) { - rcmail.filtersets_list = new rcube_list_widget(rcmail.gui_objects.filtersetslist, {multiselect:false, draggable:false, keyboard:false}); - rcmail.filtersets_list.addEventListener('select', function(e) { p.managesieve_setselect(e); }); - rcmail.filtersets_list.init(); - rcmail.filtersets_list.focus(); + rcmail.filtersets_list = new rcube_list_widget(rcmail.gui_objects.filtersetslist, + {multiselect:false, draggable:false, keyboard:false}); + + rcmail.filtersets_list + .addEventListener('select', function(e) { p.managesieve_setselect(e); }) + .init().focus(); if (set != null) { set = rcmail.managesieve_setid(set); diff --git a/plugins/zipdownload/README b/plugins/zipdownload/README index f253d63ee..e343398c3 100644 --- a/plugins/zipdownload/README +++ b/plugins/zipdownload/README @@ -2,8 +2,7 @@ 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. +download of a selection of messages in 1 zip file. Requirements ============ diff --git a/plugins/zipdownload/composer.json b/plugins/zipdownload/composer.json index 7f6e30935..cdfbf9923 100644 --- a/plugins/zipdownload/composer.json +++ b/plugins/zipdownload/composer.json @@ -1,14 +1,19 @@ { "name": "roundcube/zipdownload", "type": "roundcube-plugin", - "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": "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. Supports mbox and maildir format.", "license": "GNU GPLv3+", - "version": "2.1", + "version": "3.0", "authors": [ { "name": "Thomas Bruederli", "email": "roundcube@gmail.com", "role": "Lead" + }, + { + "name": "Aleksander Machniak", + "email": "alec@alec.pl", + "role": "Lead" } ], "repositories": [ diff --git a/plugins/zipdownload/config.inc.php.dist b/plugins/zipdownload/config.inc.php.dist index 0b2d14b60..171b4aea5 100644 --- a/plugins/zipdownload/config.inc.php.dist +++ b/plugins/zipdownload/config.inc.php.dist @@ -9,9 +9,6 @@ // -1 to prevent downloading of attachments as zip $config['zipdownload_attachments'] = 1; -// Zip entire folders -$config['zipdownload_folder'] = false; - // Zip selection of messages $config['zipdownload_selection'] = false; diff --git a/plugins/zipdownload/localization/en_US.inc b/plugins/zipdownload/localization/en_US.inc index aee8a5e15..91145205e 100644 --- a/plugins/zipdownload/localization/en_US.inc +++ b/plugins/zipdownload/localization/en_US.inc @@ -5,7 +5,7 @@ | plugins/zipdownload/localization/<lang>.inc | | | | Localization file of the Roundcube Webmail Zipdownload plugin | - | Copyright (C) 2012-2013, The Roundcube Dev Team | + | Copyright (C) 2012-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -18,6 +18,7 @@ $labels = array(); $labels['downloadall'] = 'Download all attachments'; -$labels['downloadfolder'] = 'Download folder'; - -?>
\ No newline at end of file +$labels['download'] = 'Download...'; +$labels['downloadmbox'] = 'Mbox format (.zip)'; +$labels['downloadmaildir'] = 'Maildir format (.zip)'; +$labels['downloademl'] = 'Source (.eml)'; diff --git a/plugins/zipdownload/package.xml b/plugins/zipdownload/package.xml index 3e5722045..7485c5870 100644 --- a/plugins/zipdownload/package.xml +++ b/plugins/zipdownload/package.xml @@ -6,7 +6,8 @@ <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> + <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. Supports mbox and maildir formats.</description> <lead> <name>Philip Weir</name> <user>JohnDoh</user> @@ -19,11 +20,17 @@ <email>roundcube@gmail.com</email> <active>yes</active> </lead> - <date>2014-04-07</date> - <time>17:28:00</time> + <lead> + <name>Aleksander Machniak</name> + <user>alec</user> + <email>alec@alec.pl</email> + <active>yes</active> + </lead> + <date>2014-04-18</date> + <time>09:00:00</time> <version> - <release>2.1</release> - <api>2.0</api> + <release>3.0</release> + <api>3.0</api> </version> <stability> <release>stable</release> diff --git a/plugins/zipdownload/zipdownload.js b/plugins/zipdownload/zipdownload.js index 080dcd9e3..0e0249dd3 100644 --- a/plugins/zipdownload/zipdownload.js +++ b/plugins/zipdownload/zipdownload.js @@ -2,32 +2,94 @@ * 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(',')); - } +window.rcmail && rcmail.addEventListener('init', function(evt) { + // register additional actions + rcmail.register_command('download-eml', function() { rcmail_zipdownload('eml'); }); + rcmail.register_command('download-mbox', function() { rcmail_zipdownload('mbox'); }); + rcmail.register_command('download-maildir', function() { rcmail_zipdownload('maildir'); }); + + // commands status + rcmail.message_list.addEventListener('select', function(list) { + var selected = list.get_selection().length; + + rcmail.enable_command('download', selected > 0); + rcmail.enable_command('download-eml', selected == 1); + rcmail.enable_command('download-mbox', 'download-maildir', selected > 1); + }); + + // hook before default download action + rcmail.addEventListener('beforedownload', rcmail_zipdownload_menu); + + // find and modify default download link/button + $.each(rcmail.buttons['download'] || [], function() { + var link = $('#' + this.id), + span = $('span', link); + + if (!span.length) { + span = $('<span>'); + link.html('').append(span); + } + + span.addClass('folder-selector-link').text(rcmail.gettext('zipdownload.download')); + + rcmail.env.download_link = link; + }); + + // hide menu on click out of menu element + var fn = function(e) { + var menu = $('#zipdownload-menu'); + if (e.target != menu.get(0)) + menu.hide(); + }; + $(document.body).on('mouseup', fn); + $('iframe').contents().on('mouseup', fn) + .load(function(e) { try { $(this).contents().on('mouseup', fn); } catch(e) {}; }); +}); + + +function rcmail_zipdownload(mode) +{ + // default .eml download of single message + if (mode == 'eml') { + var uid = rcmail.get_single_uid(); + rcmail.goto_url('viewsource', {_uid: uid, _mbox: rcmail.get_message_mailbox(uid), _save: 1}); + return; + } + + // multi-message download, use hidden form to POST selection + if (rcmail.message_list && rcmail.message_list.get_selection().length > 1) { + var inputs = [], form = $('#zipdownload-form'), + post = rcmail.selection_post_data(); + + post._mode = mode; + post._token = rcmail.env.request_token; + + $.each(post, function(k, v) { + inputs.push($('<input>').attr({type: 'hidden', name: k, value: v})); + }); + + if (!form.length) + form = $('<form>').attr({ + style: 'display: none', + method: 'POST', + action: '?_task=mail&_action=plugin.zipdownload.messages' + }) + .appendTo('body'); + + form.html('').append(inputs).submit(); + } } -$(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(); } ); - } -});
\ No newline at end of file +// display download options menu +function rcmail_zipdownload_menu() +{ + // fix menu style and display menu + var z_index = rcmail.env.download_link.parents('.popupmenu').css('z-index'), + menu = $('#zipdownload-menu').css({'max-height': 'none', 'z-index': z_index + 1}).show(); + + // position menu on the screen + rcmail.element_position(menu, rcmail.env.download_link); + + // abort default download action + return false; +} diff --git a/plugins/zipdownload/zipdownload.php b/plugins/zipdownload/zipdownload.php index 029eecfb8..90a314437 100644 --- a/plugins/zipdownload/zipdownload.php +++ b/plugins/zipdownload/zipdownload.php @@ -4,11 +4,13 @@ * ZipDownload * * Plugin to allow the download of all message attachments in one zip file + * and downloading of many messages in one go. * - * @version 2.1 + * @version 3.0 * @requires php_zip extension (including ZipArchive class) * @author Philip Weir * @author Thomas Bruderli + * @author Aleksander Machniak */ class zipdownload extends rcube_plugin { @@ -40,18 +42,11 @@ class zipdownload extends rcube_plugin $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')); + $this->register_action('plugin.zipdownload.attachments', array($this, 'download_attachments')); + $this->register_action('plugin.zipdownload.messages', array($this, 'download_messages')); - if (($selection = $rcmail->config->get('zipdownload_selection')) || $rcmail->config->get('zipdownload_folder')) { - $this->include_script('zipdownload.js'); - $this->api->output->set_env('zipdownload_selection', $selection); - - 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'); - } + if (!$rcmail->action && $rcmail->config->get('zipdownload_selection')) { + $this->download_menu(); } } @@ -65,7 +60,7 @@ class zipdownload extends rcube_plugin // 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)) { $href = $rcmail->url(array( - '_action' => 'plugin.zipdownload.zip_attachments', + '_action' => 'plugin.zipdownload.attachments', '_mbox' => $rcmail->output->env['mailbox'], '_uid' => $rcmail->output->env['uid'], )); @@ -92,6 +87,30 @@ class zipdownload extends rcube_plugin } /** + * Adds download options menu to the page + */ + public function download_menu() + { + $this->include_script('zipdownload.js'); + $this->add_label('download'); + + $rcmail = rcmail::get_instance(); + $menu = array(); + $ul_attr = $rcmail->config->get('skin') == 'classic' ? null : array('class' => 'toolbarmenu'); + + foreach (array('eml', 'mbox', 'maildir') as $type) { + $menu[] = html::tag('li', null, $rcmail->output->button(array( + 'command' => "download-$type", + 'label' => "zipdownload.download$type", + 'classact' => 'active', + ))); + } + + $rcmail->output->add_footer(html::div(array('id' => 'zipdownload-menu', 'class' => 'popupmenu'), + html::tag('ul', $ul_attr, implode('', $menu)))); + } + + /** * Handler for attachment download action */ public function download_attachments() @@ -153,52 +172,19 @@ class zipdownload extends rcube_plugin /** * Handler for message download action */ - public function download_selection() + public function download_messages() { - if (isset($_REQUEST['_uid'])) { - $messageset = rcmail::get_uids(); + $rcmail = rcmail::get_instance(); - if (sizeof($messageset) > 0) { + if ($rcmail->config->get('zipdownload_selection') && !empty($_POST['_uid'])) { + $messageset = rcmail::get_uids(); + if (sizeof($messageset)) { $this->_download_messages($messageset); } } } /** - * Handler for folder download action - */ - public function download_folder() - { - @set_time_limit(0); - - $imap = rcmail::get_instance()->get_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'], RCUBE_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 $header) { - if (empty($header)) - continue; - - array_push($uids, $header->uid); - } - } - } - - if (sizeof($uids) > 0) - $this->_download_messages(array($mbox_name => $uids)); - } - - /** * Helper method to packs all the given messages into a zip archive * * @param array List of message UIDs to download @@ -207,40 +193,80 @@ class zipdownload extends rcube_plugin { $rcmail = rcmail::get_instance(); $imap = $rcmail->get_storage(); + $mode = rcube_utils::get_input_value('_mode', rcube_utils::INPUT_POST); $temp_dir = $rcmail->config->get('temp_dir'); $tmpfname = tempnam($temp_dir, 'zipdownload'); $tempfiles = array($tmpfname); $folders = count($messageset) > 1; + // @TODO: file size limit + // open zip file $zip = new ZipArchive(); $zip->open($tmpfname, ZIPARCHIVE::OVERWRITE); - foreach ($messageset as $mbox => $uids){ + if ($mode == 'mbox') { + $tmpfp = fopen($tmpfname . '.mbox', 'w'); + } + + foreach ($messageset as $mbox => $uids) { $imap->set_folder($mbox); $path = $folders ? str_replace($imap->get_hierarchy_delimiter(), '/', $mbox) . '/' : ''; - foreach ($uids as $uid){ + foreach ($uids as $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); - - $disp_name = ($subject ? $subject : 'message_rfc822') . ".eml"; - $disp_name = $path . $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); + if ($mode == 'mbox') { + $from = rcube_mime::decode_address_list($headers->from, null, true, $headers->charset, true); + $from = array_shift($from); + + // Mbox format header + // @FIXME: \r\n or \n + // @FIXME: date format + $header = sprintf("From %s %s\r\n", + // replace spaces with hyphens + $from ? preg_replace('/\s/', '-', $from) : 'MAILER-DAEMON', + // internaldate + $headers->internaldate + ); + + fwrite($tmpfp, $header); + + // Use stream filter to quote "From " in the message body + stream_filter_register('mbox_filter', 'zipdownload_mbox_filter'); + $filter = stream_filter_append($tmpfp, 'mbox_filter'); + $imap->get_raw_body($uid, $tmpfp); + stream_filter_remove($filter); + fwrite($tmpfp, "\r\n"); + } + else { // maildir + $subject = rcube_mime::decode_mime_string((string)$headers->subject); + $subject = $this->_convert_filename($subject); + $subject = substr($subject, 0, 16); + + $disp_name = ($subject ? $subject : 'message_rfc822') . ".eml"; + $disp_name = $path . $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); + } } } + $filename = $folders ? 'messages' : $imap->get_folder(); + + if ($mode == 'mbox') { + $tempfiles[] = $tmpfname . '.mbox'; + fclose($tmpfp); + $zip->addFile($tmpfname . '.mbox', $filename . '.mbox'); + } + $zip->close(); - $filename = $folders ? 'messages' : $imap->get_folder(); $this->_deliver_zipfile($tmpfname, $filename . '.zip'); // delete temporary files from disk @@ -261,9 +287,7 @@ class zipdownload extends rcube_plugin $rcmail->output->nocacheing_headers(); - if ($browser->ie && $browser->ver < 7) - $filename = rawurlencode(abbreviate_string($filename, 55)); - else if ($browser->ie) + if ($browser->ie) $filename = rawurlencode($filename); else $filename = addcslashes($filename, '"'); @@ -288,6 +312,25 @@ class zipdownload extends rcube_plugin { $str = rcube_charset::convert($str, RCUBE_CHARSET, $this->charset); - return strtr($str, array(':'=>'', '/'=>'-')); + return strtr($str, array(':' => '', '/' => '-')); + } +} + +class zipdownload_mbox_filter extends php_user_filter +{ + function filter($in, $out, &$consumed, $closing) + { + while ($bucket = stream_bucket_make_writeable($in)) { + // messages are read line by line + if (preg_match('/^>*From /', $bucket->data)) { + $bucket->data = '>' . $bucket->data; + $bucket->datalen += 1; + } + + $consumed += $bucket->datalen; + stream_bucket_append($out, $bucket); + } + + return PSFS_PASS_ON; } } diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 1fd077665..8d7101e85 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -1622,7 +1622,7 @@ class rcmail extends rcube $count = count($path); if ($count > 1) { - for ($i = 0; $i < $count; $i++) { + for ($i = 1; $i < $count; $i++) { $folder = implode($delimiter, array_slice($path, 0, -$i)); if ($folder_class = $this->folder_classname($folder)) { $name = implode($delimiter, array_slice($path, $count - $i)); diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index a23b8405e..c3232b246 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -519,25 +519,12 @@ class rcmail_output_html extends rcmail_output $output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output); $this->footer = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $this->footer); - if ($write) { - // add debug console - if ($realname != 'error' && ($this->config->get('debug_level') & 8)) { - $this->add_footer('<div id="console" style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;z-index:9000;display:none"> - <a href="#toggle" onclick="con=$(\'#dbgconsole\');con[con.is(\':visible\')?\'hide\':\'show\']();return false">console</a> - <textarea name="console" id="dbgconsole" rows="20" cols="40" style="display:none;width:400px;border:none;font-size:10px" spellcheck="false"></textarea></div>' - ); - $this->add_script( - "if (!window.console || !window.console.log) {\n". - " window.console = new rcube_console();\n". - " $('#console').show();\n". - "}", 'foot'); - } - $this->write(trim($output)); - } - else { + if (!$write) { return $output; } + $this->write(trim($output)); + if ($exit) { exit; } diff --git a/program/js/app.js b/program/js/app.js index 2cc14a79e..4a5200028 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -143,7 +143,7 @@ function rcube_webmail() this.task = this.env.task; // check browser - if (!bw.dom || !bw.xmlhttp_test() || (bw.mz && bw.vendver < 1.9) || (bw.ie && bw.vendver < 7)) { + if (this.env.server_error != 409 && (!bw.dom || !bw.xmlhttp_test() || (bw.mz && bw.vendver < 1.9) || (bw.ie && bw.vendver < 7))) { this.goto_url('error', '_code=0x199'); return; } @@ -1074,8 +1074,9 @@ function rcube_webmail() // Reset the auto-save timer clearTimeout(this.save_timer); - if (!this.upload_file(props || this.gui_objects.uploadform, 'upload')) { - alert(this.get_label('selectimportfile')); + if (!(flag = this.upload_file(props || this.gui_objects.uploadform, 'upload'))) { + if (flag !== false) + alert(this.get_label('selectimportfile')); aborted = true; } break; @@ -1200,12 +1201,15 @@ function rcube_webmail() break; case 'import-messages': - var form = props || this.gui_objects.importform; - var importlock = this.set_busy(true, 'importwait'); + var form = props || this.gui_objects.importform, + importlock = this.set_busy(true, 'importwait'); + $('input[name="_unlock"]', form).val(importlock); - if (!this.upload_file(form, 'import')) { + + if (!(flag = this.upload_file(form, 'import'))) { this.set_busy(false, null, importlock); - alert(this.get_label('selectimportfile')); + if (flag !== false) + alert(this.get_label('selectimportfile')); aborted = true; } break; @@ -1936,7 +1940,7 @@ function rcube_webmail() // add each submitted col for (n in this.env.listcols) { c = this.env.listcols[n]; - col = { className: String(c).toLowerCase() }; + col = {className: String(c).toLowerCase(), events:{}}; if (this.env.coltypes[c] && this.env.coltypes[c].hidden) { col.className += ' hidden'; @@ -1970,11 +1974,8 @@ function rcube_webmail() else if (c == 'threads') html = expando; else if (c == 'subject') { - if (bw.ie) { - col.onmouseover = function() { rcube_webmail.long_subject_title_ex(this, message.depth+1); }; - if (bw.ie8) - tree = '<span></span>' + tree; // #1487821 - } + if (bw.ie) + col.events.mouseover = function() { rcube_webmail.long_subject_title_ex(this); }; html = tree + cols[c]; } else if (c == 'priority') { @@ -4019,7 +4020,7 @@ function rcube_webmail() this.upload_file = function(form, action) { if (!form) - return false; + return; // count files and size on capable browser var size = 0, numfiles = 0; @@ -4079,8 +4080,6 @@ function rcube_webmail() this.gui_objects.attachmentform = form; return true; } - - return false; }; // add file name to attachment list @@ -4102,7 +4101,7 @@ function rcube_webmail() li.attr('id', name) .addClass(att.classname) .html(att.html) - .on('mouseover', function() { rcube_webmail.long_subject_title_ex(this, 0); }); + .on('mouseover', function() { rcube_webmail.long_subject_title_ex(this); }); // replace indicator's li if (upload_id && (indicator = document.getElementById(upload_id))) { @@ -6846,7 +6845,7 @@ function rcube_webmail() param[k] = query[k]; } - return base + '&' + $.param(param) + querystring; + return base + (base.indexOf('?') > -1 ? '&' : '?') + $.param(param) + querystring; }; this.redirect = function(url, lock) @@ -7862,7 +7861,7 @@ rcube_webmail.long_subject_title = function(elem, indent) } }; -rcube_webmail.long_subject_title_ex = function(elem, indent) +rcube_webmail.long_subject_title_ex = function(elem) { if (!elem.title) { var $elem = $(elem), @@ -7874,7 +7873,7 @@ rcube_webmail.long_subject_title_ex = function(elem, indent) w = tmp.width(); tmp.remove(); - if (w + indent * 15 > $elem.width()) + if (w + $('span.branch', $elem).width() * 15 > $elem.width()) elem.title = txt; } }; diff --git a/program/js/common.js b/program/js/common.js index ed9488b2c..28f79d56f 100644 --- a/program/js/common.js +++ b/program/js/common.js @@ -255,13 +255,17 @@ remove_listener: function(p) cancel: function(evt) { var e = evt ? evt : window.event; + if (e.preventDefault) e.preventDefault(); + else + e.returnValue = false; + if (e.stopPropagation) e.stopPropagation(); e.cancelBubble = true; - e.returnValue = false; + return false; }, @@ -326,13 +330,17 @@ removeEventListener: function(evt, func, obj) triggerEvent: function(evt, e) { var ret, h; + if (e === undefined) e = this; else if (typeof e === 'object') e.event = evt; - if (this._events && this._events[evt] && !this._event_exec) { - this._event_exec = true; + if (!this._event_exec) + this._event_exec = {}; + + if (this._events && this._events[evt] && !this._event_exec[evt]) { + this._event_exec[evt] = true; for (var i=0; i < this._events[evt].length; i++) { if ((h = this._events[evt][i])) { if (typeof h.func === 'function') @@ -355,7 +363,8 @@ triggerEvent: function(evt, e) } } - this._event_exec = false; + delete this._event_exec[evt]; + if (e.event) { try { delete e.event; @@ -529,36 +538,6 @@ function getCookie(name) roundcube_browser.prototype.set_cookie = setCookie; roundcube_browser.prototype.get_cookie = getCookie; -// tiny replacement for Firebox functionality -function rcube_console() -{ - this.log = function(msg) - { - var box = rcube_find_object('dbgconsole'); - - if (box) { - if (msg.charAt(msg.length-1)=='\n') - msg += '--------------------------------------\n'; - else - msg += '\n--------------------------------------\n'; - - // Konqueror doesn't allow to just change the value of hidden element - if (bw.konq) { - box.innerText += msg; - box.value = box.innerText; - } else - box.value += msg; - } - }; - - this.reset = function() - { - var box = rcube_find_object('dbgconsole'); - if (box) - box.innerText = box.value = ''; - }; -}; - var bw = new roundcube_browser(); bw.set_html_class(); @@ -596,20 +575,6 @@ if (!String.prototype.startsWith) { }; } -// Make getElementById() case-sensitive on IE -if (bw.ie) { - document._getElementById = document.getElementById; - document.getElementById = function(id) { - var i = 0, obj = document._getElementById(id); - - if (obj && obj.id != id) - while ((obj = document.all[i]) && obj.id != id) - i++; - - return obj; - } -} - // jQuery plugin to emulate HTML5 placeholder attributes on input elements jQuery.fn.placeholder = function(text) { return this.each(function() { diff --git a/program/js/list.js b/program/js/list.js index 9b7779c7b..a39bb5abe 100644 --- a/program/js/list.js +++ b/program/js/list.js @@ -301,11 +301,13 @@ insert_row: function(row, before) if (row.style) $.extend(domrow.style, row.style); if (row.uid) $(domrow).data('uid', row.uid); - for (var domcell, col, i=0; row.cols && i < row.cols.length; i++) { + for (var e, domcell, col, i=0; row.cols && i < row.cols.length; i++) { col = row.cols[i]; domcell = document.createElement(this.col_tagname()); if (col.className) domcell.className = col.className; if (col.innerHTML) domcell.innerHTML = col.innerHTML; + for (e in col.events) + domcell['on' + e] = col.events[e]; domrow.appendChild(domcell); } diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php index aa385dce4..06bc387d5 100644 --- a/program/lib/Roundcube/rcube_csv2vcard.php +++ b/program/lib/Roundcube/rcube_csv2vcard.php @@ -56,7 +56,7 @@ class rcube_csv2vcard //'email_2_type' => '', //'email_3_address' => '', //@TODO //'email_3_type' => '', - 'email_address' => 'email:main', + 'email_address' => 'email:pref', //'email_type' => '', 'first_name' => 'firstname', 'gender' => 'gender', diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 5c30327a1..baca052b8 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -1544,20 +1544,27 @@ class rcube_imap extends rcube_storage */ public function search_once($folder = null, $str = 'ALL') { + if (!$this->check_connection()) { + return new rcube_result_index(); + } + if (!$str) { $str = 'ALL'; } - if (!strlen($folder)) { - $folder = $this->folder; + // multi-folder search + if (is_array($folder) && count($folder) > 1) { + $searcher = new rcube_imap_search($this->options, $this->conn); + $index = $searcher->exec($folder, $str, $this->default_charset); } - - if (!$this->check_connection()) { - return new rcube_result_index(); + else { + $folder = is_array($folder) ? $folder[0] : $folder; + if (!strlen($folder)) { + $folder = $this->folder; + } + $index = $this->conn->search($folder, $str, true); } - $index = $this->conn->search($folder, $str, true); - return $index; } @@ -1707,15 +1714,15 @@ class rcube_imap extends rcube_storage */ public function get_message_headers($uid, $folder = null, $force = false) { - if (!strlen($folder)) { - $folder = $this->folder; - } - // decode combined UID-folder identifier if (preg_match('/^\d+-.+/', $uid)) { list($uid, $folder) = explode('-', $uid, 2); } + if (!strlen($folder)) { + $folder = $this->folder; + } + // get cached headers if (!$force && $uid && ($mcache = $this->get_mcache_engine())) { $headers = $mcache->get_message($folder, $uid); @@ -1726,6 +1733,9 @@ class rcube_imap extends rcube_storage else { $headers = $this->conn->fetchHeader( $folder, $uid, true, true, $this->get_fetch_headers()); + + if (is_object($headers)) + $headers->folder = $folder; } return $headers; diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 8f869c67c..c6262097f 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -293,9 +293,7 @@ else if (strlen($part_id)) { $filename = rcmail_attachment_name($part); - if ($browser->ie && $browser->ver < 7) - $filename = rawurlencode(abbreviate_string($filename, 55)); - else if ($browser->ie) + if ($browser->ie) $filename = rawurlencode($filename); else $filename = addcslashes($filename, '"'); diff --git a/program/steps/mail/import.inc b/program/steps/mail/import.inc index 217927537..ef78ad945 100644 --- a/program/steps/mail/import.inc +++ b/program/steps/mail/import.inc @@ -5,7 +5,7 @@ | program/steps/mail/import.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | Copyright (C) 2005-2014, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -16,6 +16,7 @@ | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli <roundcube@gmail.com> | + | Author: Aleksander Machniak <alec@alec.pl> | +-----------------------------------------------------------------------+ */ @@ -31,46 +32,62 @@ if (is_array($_FILES['_file'])) { if (!$err) { // check file content type first - list($mtype_primary,) = explode('/', rcube_mime::file_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i])); + $ctype = rcube_mime::file_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i]); + list($mtype_primary, $mtype_secondary) = explode('/', $ctype); - if (!in_array($mtype_primary, array('text','message'))) { + if (in_array($ctype, array('application/zip', 'application/x-zip'))) { + $filepath = rcmail_zip_extract($filepath); + if (empty($filepath)) { + continue; + } + } + else if (!in_array($mtype_primary, array('text', 'message'))) { continue; } - // read the first few lines to detect header-like structure - $fp = fopen($filepath, 'r'); - do { - $line = fgets($fp); - } - while ($line !== false && trim($line) == ''); + foreach ((array) $filepath as $file) { + // read the first few lines to detect header-like structure + $fp = fopen($file, 'r'); + do { + $line = fgets($fp); + } + while ($line !== false && trim($line) == ''); - if (!preg_match('/^From\s+-/', $line) && !preg_match('/^[a-z-_]+:\s+.+/i', $line)) { - continue; - } + if (!preg_match('/^From .+/', $line) && !preg_match('/^[a-z-_]+:\s+.+/i', $line)) { + continue; + } - $message = $lastline = ''; - fseek($fp, 0); - while (($line = fgets($fp)) !== false) { - // importing mbox file, split by From - lines - if (preg_match('/^From\s+-/', $line) && ($lastline == '' || substr($lastline, -2) == '--')) { - if (!empty($message)) { - if ($RCMAIL->storage->save_message(null, rtrim($message))) { - $imported++; - } - else { - rcube::raise_error("Failed to import message to " . $RCMAIL->storage->get_folder(), false, true); + $message = $lastline = ''; + fseek($fp, 0); + while (($line = fgets($fp)) !== false) { + // importing mbox file, split by From - lines + if ($lastline === '' && strncmp($line, 'From ', 5) === 0 && strlen($line) > 5) { + if (!empty($message)) { + // unquote ">From " lines in message body + $message = preg_replace('/\n>([>]*)From /', "\n\\1From ", $message); + if ($RCMAIL->storage->save_message(null, rtrim($message))) { + $imported++; + } + else { + rcube::raise_error("Failed to import message to " . $RCMAIL->storage->get_folder(), false, true); + } + $message = ''; } - $message = ''; + continue; } - continue; + + $message .= $line; + $lastline = rtrim($line); } - $message .= $line; - $lastline = rtrim($line); - } + if (!empty($message) && $RCMAIL->storage->save_message(null, rtrim($message))) { + $imported++; + } - if (!empty($message) && $RCMAIL->storage->save_message(null, rtrim($message))) { - $imported++; + // remove temp files extracted from zip + if (is_array($filepath)) { + unlink($file); + } } } @@ -104,3 +121,39 @@ else if ($_SERVER['REQUEST_METHOD'] == 'POST') { // send html page with JS calls as response $OUTPUT->send('iframe'); + + +function rcmail_zip_extract($path) +{ + if (!class_exists('ZipArchive', false)) { + return; + } + + $rcmail = rcmail::get_instance(); + $temp_dir = $rcmail->config->get('temp_dir'); + $zip = new ZipArchive; + $files = array(); + + if ($zip->open($path)) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $entry = $zip->getNameIndex($i); + $tmpfname = tempnam($temp_dir, 'zipimport'); + + if (copy("zip://$path#$entry", $tmpfname)) { + $ctype = rcube_mime::file_content_type($tmpfname, $entry); + list($mtype_primary, $mtype_secondary) = explode('/', $ctype); + + if (in_array($mtype_primary, array('text', 'message'))) { + $files[] = $tmpfname; + } + else { + unlink($tmpfname); + } + } + } + + $zip->close(); + } + + return $files; +} diff --git a/program/steps/mail/viewsource.inc b/program/steps/mail/viewsource.inc index 0328d9600..f988f679a 100644 --- a/program/steps/mail/viewsource.inc +++ b/program/steps/mail/viewsource.inc @@ -33,9 +33,7 @@ if ($uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GET)) { $filename = ($subject ? $subject : $RCMAIL->config->get('product_name', 'email')) . '.eml'; $browser = $RCMAIL->output->browser; - if ($browser->ie && $browser->ver < 7) - $filename = rawurlencode(abbreviate_string($filename, 55)); - else if ($browser->ie) + if ($browser->ie) $filename = rawurlencode($filename); else $filename = addcslashes($filename, '"'); diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 47efa5a70..bccd9caa8 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -346,6 +346,7 @@ function rcmail_user_prefs($current = null) $license_link = $meta['license-url'] ? html::a(array('href' => $meta['license-url'], 'target' => '_blank'), rcube::Q($meta['license'])) : rcube::Q($meta['license']); } + $skinnames[] = mb_strtolower($skinname); $blocks['skin']['options'][$skin]['content'] = html::label(array('class' => 'skinselection'), html::span('skinitem', $input->show($config['skin'], array('value' => $skin, 'id' => $field_id.$skin))) . html::span('skinitem', html::img(array('src' => $thumbnail, 'class' => 'skinthumbnail', 'alt' => $skin, 'width' => 64, 'height' => 64))) . @@ -354,6 +355,7 @@ function rcmail_user_prefs($current = null) html::span('skinlicense', $license_link ? $RCMAIL->gettext('license').': ' . $license_link : '')) ); } + array_multisort($blocks['skin']['options'], SORT_ASC, SORT_STRING, $skinnames); } } diff --git a/skins/classic/common.css b/skins/classic/common.css index 30370205a..4367d26cb 100644 --- a/skins/classic/common.css +++ b/skins/classic/common.css @@ -941,11 +941,6 @@ a.rcmContactAddress:hover font-weight: bold; } -#console -{ - opacity: 0.8; -} - .disabled, a.disabled { |