From ed1ceea87829093ddd62bf214e56213bf4d91c2f Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 18 Apr 2014 04:13:51 -0400
Subject: [PATCH] Add possibility to download messages in mbox format (#1486069) Removed "download folder" option, the same can be done by selecting all messages in a folder Code refactoring

---
 plugins/zipdownload/README                 |    3 
 plugins/zipdownload/composer.json          |    9 +
 plugins/zipdownload/zipdownload.js         |  112 ++++++++++++++----
 plugins/zipdownload/localization/en_US.inc |    9 
 plugins/zipdownload/zipdownload.php        |  181 ++++++++++++++++++-----------
 plugins/zipdownload/package.xml            |   17 ++
 plugins/zipdownload/config.inc.php.dist    |    3 
 7 files changed, 224 insertions(+), 110 deletions(-)

diff --git a/plugins/zipdownload/README b/plugins/zipdownload/README
index f253d63..e343398 100644
--- a/plugins/zipdownload/README
+++ b/plugins/zipdownload/README
@@ -2,8 +2,7 @@
 =============================
 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 7f6e309..cdfbf99 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 0b2d14b..171b4ae 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 aee8a5e..9114520 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 3e57220..7485c58 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 080dcd9..0e0249d 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);
+// 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();
 
-			if (rcmail.message_list && rcmail.env.zipdownload_selection) {
-				rcmail.message_list.addEventListener('select', function(list) {
-					rcmail.enable_command('download', list.get_selection().length > 0);
-				});
+    // position menu on the screen
+    rcmail.element_position(menu, rcmail.env.download_link);
 
-				// 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
+    // abort default download action
+    return false;
+}
diff --git a/plugins/zipdownload/zipdownload.php b/plugins/zipdownload/zipdownload.php
index 029eecf..90a3144 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 @@
             $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 @@
         // 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'],
             ));
@@ -89,6 +84,30 @@
         }
 
         return $p;
+    }
+
+    /**
+     * 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))));
     }
 
     /**
@@ -153,49 +172,16 @@
     /**
      * 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));
     }
 
     /**
@@ -207,40 +193,80 @@
     {
         $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;
+                if ($mode == 'mbox') {
+                    $from = rcube_mime::decode_address_list($headers->from, null, true, $headers->charset, true);
+                    $from = array_shift($from);
 
-                $tmpfn = tempnam($temp_dir, 'zipmessage');
-                $tmpfp = fopen($tmpfn, 'w');
-                $imap->get_raw_body($uid, $tmpfp);
-                $tempfiles[] = $tmpfn;
-                fclose($tmpfp);
-                $zip->addFile($tmpfn, $disp_name);
+                    // 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 @@
 
         $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 @@
     {
         $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;
     }
 }

--
Gitblit v1.9.1