From 4f53ab347e6ad026f69105b69ce66e21a7654c78 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Thu, 13 Jun 2013 19:13:13 -0400
Subject: [PATCH] Add feature to import messages to the currently selected folder

---
 skins/larry/templates/mail.html         |   11 ++
 program/steps/mail/import.inc           |  105 ++++++++++++++++++++++++++
 program/localization/en_US/messages.inc |    2 
 program/steps/mail/func.inc             |   39 +++++++++
 program/localization/en_US/labels.inc   |    1 
 program/lib/Roundcube/html.php          |    4 
 program/js/app.js                       |   19 +++-
 skins/larry/ui.js                       |    1 
 8 files changed, 172 insertions(+), 10 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 0dea592..37aee8d 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -191,7 +191,7 @@
 
       case 'mail':
         // enable mail commands
-        this.enable_command('list', 'checkmail', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);
+        this.enable_command('list', 'checkmail', 'add-contact', 'search', 'reset-search', 'collapse-folder', 'import-messages', true);
 
         if (this.gui_objects.messagelist) {
           this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {
@@ -277,11 +277,12 @@
           this.init_messageform();
         }
         // show printing dialog
-        else if (this.env.action == 'print' && this.env.uid)
+        else if (this.env.action == 'print' && this.env.uid) {
           if (bw.safari)
             setTimeout('window.print()', 10);
           else
             window.print();
+        }
 
         // get unread count for each mailbox
         if (this.gui_objects.mailboxlist) {
@@ -1000,7 +1001,7 @@
         // Reset the auto-save timer
         clearTimeout(this.save_timer);
 
-        this.upload_file(props || this.gui_objects.uploadform);
+        this.upload_file(props || this.gui_objects.uploadform, 'upload');
         break;
 
       case 'insert-sig':
@@ -1099,6 +1100,12 @@
       case 'listgroup':
         this.reset_qsearch();
         this.list_contacts(props.source, props.id);
+        break;
+
+      case 'import-messages':
+        var form = props || this.gui_objects.importform;
+        $('input[name="_unlock"]', form).val(this.set_busy(true, 'importwait'));
+        this.upload_file(form, 'import');
         break;
 
       case 'import':
@@ -3492,8 +3499,8 @@
     return true;
   };
 
-  // upload attachment file
-  this.upload_file = function(form)
+  // upload (attachment) file
+  this.upload_file = function(form, action)
   {
     if (!form)
       return false;
@@ -3520,7 +3527,7 @@
         return;
       }
 
-      var frame_name = this.async_upload_form(form, 'upload', function(e) {
+      var frame_name = this.async_upload_form(form, action || 'upload', function(e) {
         var d, content = '';
         try {
           if (this.contentDocument) {
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index eb23c8b..3e6e47a 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -360,8 +360,8 @@
     protected $allowed = array(
         'type','name','value','size','tabindex','autocapitalize',
         'autocomplete','checked','onchange','onclick','disabled','readonly',
-        'spellcheck','results','maxlength','src','multiple','placeholder',
-        'autofocus',
+        'spellcheck','results','maxlength','src','multiple','accept',
+        'placeholder','autofocus',
     );
 
     /**
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index d8e5173..c5e6cae 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -194,6 +194,7 @@
 $labels['folderactions'] = 'Folder actions...';
 $labels['compact'] = 'Compact';
 $labels['empty'] = 'Empty';
+$labels['importmessages'] = 'Import messages';
 
 $labels['quota'] = 'Disk usage';
 $labels['unknown']  = 'unknown';
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index f9b5e00..16f4c67 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -126,6 +126,8 @@
 $messages['importformaterror'] = 'Import failed! The uploaded file is not a valid import data file.';
 $messages['importconfirm'] = '<b>Successfully imported $inserted contacts</b>';
 $messages['importconfirmskipped'] = '<b>Skipped $skipped existing entries</b>';
+$messages['importmessagesuccess'] = 'Successfully imported $nr messages';
+$messages['importmessageerror'] = 'Import failed! The uploaded file is not a valid message or mailbox file';
 $messages['opnotpermitted'] = 'Operation not permitted!';
 $messages['nofromaddress'] = 'Missing e-mail address in selected identity.';
 $messages['editorwarning'] = 'Switching to the plain text editor will cause all text formatting to be lost. Do you wish to continue?';
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 1a687f5..7b6a482 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -120,7 +120,7 @@
   if (!$OUTPUT->ajax_call)
     $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash',
       'movingmessage', 'copyingmessage', 'deletingmessage', 'markingmessage',
-      'copy', 'move', 'quota', 'replyall', 'replylist');
+      'copy', 'move', 'quota', 'replyall', 'replylist', 'importwait');
 
   $pagetitle = $RCMAIL->localize_foldername($RCMAIL->storage->mod_folder($mbox_name), true);
   $pagetitle = str_replace($delimiter, " \xC2\xBB ", $pagetitle);
@@ -1922,6 +1922,42 @@
   $RCMAIL->output->send('messageerror');
 }
 
+function rcmail_message_import_form($attrib = array())
+{
+  global $OUTPUT;
+
+  // set defaults
+  $attrib += array('id' => 'rcmImportform', 'buttons' => 'yes');
+
+  // Get filesize, enable upload progress bar
+  $max_filesize = rcube_upload_init();
+
+  $button = new html_inputfield(array('type' => 'button'));
+  $fileinput = new html_inputfield(array(
+      'type' => 'file',
+      'name' => '_file[]',
+      'size' => $attrib['attachmentfieldsize'],
+      'multiple' => 'multiple',
+      'accept' => ".eml, .mbox, message/rfc822, text/*",
+  ));
+
+  $out = html::div($attrib,
+    $OUTPUT->form_tag(array('id' => $attrib['id'].'Frm', 'method' => 'post', 'enctype' => 'multipart/form-data'),
+      html::tag('input', array('type' => 'hidden', 'name' => '_unlock', 'value' => '')) .
+      html::div(null, $fileinput->show()) .
+      html::div('hint', rcube_label(array('name' => 'maxuploadsize', 'vars' => array('size' => $max_filesize)))) .
+      (get_boolean($attrib['buttons']) ? html::div('buttons',
+        $button->show(rcube_label('close'), array('class' => 'button', 'onclick' => "$('#$attrib[id]').hide()")) . ' ' .
+        $button->show(rcube_label('upload'), array('class' => 'button mainaction', 'onclick' => JS_OBJECT_NAME . ".command('import-messages', this.form)"))
+      ) : '')
+    )
+  );
+
+  $OUTPUT->add_gui_object('importform', $attrib['id'].'Frm');
+  return $out;
+}
+
+
 // register UI objects
 $OUTPUT->add_handlers(array(
   'mailboxlist' => 'rcmail_mailbox_list',
@@ -1935,6 +1971,7 @@
   'messagecontentframe' => 'rcmail_messagecontent_frame',
   'messagepartframe' => 'rcmail_message_part_frame',
   'messagepartcontrols' => 'rcmail_message_part_controls',
+  'messageimportform' => 'rcmail_message_import_form',
   'searchfilter' => 'rcmail_search_filter',
   'searchform' => array($OUTPUT, 'search_form'),
 ));
diff --git a/program/steps/mail/import.inc b/program/steps/mail/import.inc
new file mode 100644
index 0000000..f7e7a3e
--- /dev/null
+++ b/program/steps/mail/import.inc
@@ -0,0 +1,105 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/import.inc                                         |
+ |                                                                       |
+ | This file is part of the Roundcube Webmail client                     |
+ | Copyright (C) 2005-2013, The Roundcube Dev Team                       |
+ |                                                                       |
+ | Licensed under the GNU General Public License version 3 or            |
+ | any later version with exceptions for skins & plugins.                |
+ | See the README file for a full license statement.                     |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Save the uploaded file(s) as messages to the current IMAP folder    |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+*/
+
+// clear all stored output properties (like scripts and env vars)
+$OUTPUT->reset();
+
+if (is_array($_FILES['_file'])) {
+    $imported = 0;
+
+    foreach ((array)$_FILES['_file']['tmp_name'] as $i => $filepath) {
+        // Process uploaded file if there is no error
+        $err = $_FILES['_file']['error'][$i];
+
+        if (!$err) {
+            // check file content type first
+            list($mtype_primary,) = explode('/', rc_mime_content_type($filepath, $_FILES['_file']['name'][$i], $_FILES['_file']['type'][$i]));
+            if (!in_array($mtype_primary, array('text','message'))) {
+                $OUTPUT->show_message('importmessageerror', 'error');
+                continue;
+            }
+
+            // read the first few lines to detect header-like structure
+            $fp = fopen($filepath, 'r');
+            do { $line = fgets($fp); }
+            while ($line !== false && trim($line) == '');
+
+            if (!preg_match('/^From\s+-/', $line) && !preg_match('/^[a-z-_]+:\s+.+/i', $line)) {
+                $OUTPUT->show_message('importmessageerror', 'error');
+                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 == '') {
+                    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 = '';
+                    }
+                    continue;
+                }
+
+                $message .= $line;
+                $lastline = rtrim($line);
+            }
+
+            if (!empty($message) && $RCMAIL->storage->save_message(null, rtrim($message))) {
+                $imported++;
+            }
+        }
+
+        if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
+            $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
+        }
+        else if ($err) {
+            $OUTPUT->show_message('fileuploaderror', 'error');
+        }
+    }  // end foreach
+
+    if ($imported) {
+        $OUTPUT->show_message(rcube_label(array('name' => 'importmessagesuccess', 'nr' => $imported, 'vars' => array('nr' => $imported))), 'confirmation');
+        $OUTPUT->command('command', 'list');
+    }
+    else {
+        $OUTPUT->show_message('importmessageerror', 'error');
+    }
+}
+else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+    // if filesize exceeds post_max_size then $_FILES array is empty,
+    // show filesizeerror instead of fileuploaderror
+    if ($maxsize = ini_get('post_max_size'))
+        $msg = rcube_label(array('name' => 'filesizeerror', 'vars' => array('size' => show_bytes(parse_bytes($maxsize)))));
+    else
+        $msg = rcube_label('fileuploaderror');
+
+    $OUTPUT->command('display_message', $msg, 'error');
+}
+
+// send html page with JS calls as response
+$OUTPUT->send('iframe');
+
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index 85cd520..575cb79 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -148,7 +148,8 @@
 <div id="mailboxmenu" class="popupmenu">
 	<ul class="toolbarmenu" id="mailboxoptionsmenu">
 		<li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
-		<li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
+		<li><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
+		<li><roundcube:button name="messageimport" type="link" class="active" label="importmessages" onclick="UI.show_uploadform()" /></li>
 		<li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
 		<roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
 	</ul>
@@ -226,6 +227,14 @@
 	</div>
 </div>
 
+<div id="upload-dialog" class="propform popupdialog" title="<roundcube:label name='importmessages' />">
+	<roundcube:object name="messageimportform" id="uploadform" attachmentFieldSize="40" buttons="no" />
+	<div class="formbuttons">
+		<roundcube:button command="import-messages" type="input" class="button mainaction" label="upload" />
+		<roundcube:button name="close" type="input" class="button" label="cancel" onclick="UI.show_uploadform()" />
+	</div>
+</div>
+
 <roundcube:include file="/includes/footer.html" />
 
 </body>
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index f7428f4..ec4d03d 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -152,6 +152,7 @@
 
         rcmail.addEventListener('setquota', update_quota);
         rcmail.addEventListener('enable-command', enable_command);
+        rcmail.addEventListener('afterimport-messages', show_uploadform);
       }
 
       if ($('#mailview-left').length) {

--
Gitblit v1.9.1