From 1cd3762b0d7707f4dd665c00ff4d83db6172b4a7 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Mon, 25 May 2015 12:51:33 -0400
Subject: [PATCH] Start integrating the Mailvelope browser extension via its API.

---
 program/localization/en_US/messages.inc |    1 
 program/steps/mail/func.inc             |   23 +++++
 program/js/app.js                       |  141 +++++++++++++++++++++++++++++++++++
 skins/larry/mail.css                    |    9 ++
 skins/larry/ui.js                       |    8 ++
 5 files changed, 181 insertions(+), 1 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index dc5c8c1..4cb6153 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -376,6 +376,8 @@
           this.http_post(postact, postdata);
         }
 
+        this.check_mailvelope(this.env.action);
+
         // detect browser capabilities
         if (!this.is_framed() && !this.env.extwin)
           this.browser_capabilities_check();
@@ -3341,6 +3343,141 @@
     $('input.rcpagejumper').val(this.env.current_page).prop('disabled', this.env.pagecount < 2);
   };
 
+  // check for mailvelope API
+  this.check_mailvelope = function(action)
+  {
+    if (typeof window.mailvelope !== 'undefined') {
+      this.mailvelope_init(action);
+    }
+    else {
+      $(window).on('mailvelope', function() {
+        ref.mailvelope_init(action);
+      });
+    }
+  };
+
+  // 
+  this.mailvelope_init = function(action)
+  {
+    if (this.env.browser_capabilities)
+      this.env.browser_capabilities['pgpmime'] = 1;
+
+    var keyring = this.get_local_storage_prefix();
+
+    mailvelope.getKeyring(keyring).then(function(kr) {
+      ref.mailvelope_keyring = kr;
+    }, function(err) {
+      // attempt to create a new keyring for this app/user
+      mailvelope.createKeyring(keyring).then(function(kr) {
+        ref.mailvelope_keyring = kr;
+        keyring = keyring.identifier;
+      }, function(err) {
+        console.error(err)
+      });
+    });
+
+    if (action == 'show' || action == 'preview') {
+      // decrypt text body
+      if (this.env.is_pgp_content && window.mailvelope) {
+        var data = $(this.env.is_pgp_content).text();
+        ref.mailvelope_display_container(this.env.is_pgp_content, data, keyring);
+      }
+      // load pgp/mime message and pass it to the mailvelope display container
+      else if (this.env.pgp_mime_part && window.mailvelope) {
+        var msgid = this.display_message(this.get_label('loadingdata'), 'loading'),
+          selector = this.env.pgp_mime_container;
+
+        $.ajax({
+          type: 'GET',
+          url: this.url('get', { '_mbox': this.env.mailbox, '_uid': this.env.uid, '_part': this.env.pgp_mime_part }),
+          error: function(o, status, err) {
+            ref.hide_message(msgkey);
+            ref.http_error(o, status, err, lock);
+          },
+          success: function(data) {
+            ref.mailvelope_display_container(selector, data, keyring, msgid);
+          }
+        });
+      }
+    }
+    else if (action == 'compose' && window.mailvelope) {
+      this.enable_command('compose-encrypted', true);
+    }
+  };
+
+  // handler for the 'compose-encrypt' command
+  this.compose_encrypted = function(props)
+  {
+    var container = $('#' + this.env.composebody).parent();
+    mailvelope.createEditorContainer('#' + container.attr('id'), keyring).then(function(editor) {
+      ref.mailvelope_editor = editor;
+      container.addClass('mailvelope');
+      $('#' + ref.env.composebody).hide();
+    });
+  };
+
+  // callback to replace the message body with the full armored
+  this.mailvelope_submit_messageform = function(draft, saveonly)
+  {
+    // get recipients
+    var recipients = [];
+    $.each(['to', 'cc', 'bcc'], function(i,field) {
+      var pos, rcpt, val = $.trim($('[name="_' + field + '"]').val());
+      while (val.length && rcube_check_email(val, true)) {
+        rcpt = RegExp.$2
+        recipients.push(rcpt);
+        val = val.substr(val.indexOf(rcpt) + rcpt.length + 1).replace(/^\s*,\s*/, '');
+        console.log('*', val)
+      }
+    });
+
+    // check if we have keys for all recipients
+    var isvalid = recipients.length > 0;
+    ref.mailvelope_keyring.validKeyForAddress(recipients).then(function(status) {
+      $.each(status, function(k,v) {
+        console.log('validate', k, v)
+        if (!v) {
+          isvalid = false;
+          alert("No key found for "+k)
+        }
+      });
+
+      if (!isvalid) {
+        if (!recipients.length)
+          alert(ref.get_label('norecipientwarning'));
+        return false;
+      }
+
+      ref.mailvelope_editor.encrypt(recipients).then(function(armored) {
+        console.log('encrypted message', armored);
+        var form = this.gui_objects.messageform;
+
+        // all checks passed, send message
+        // var msgid = ref.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage')
+
+      }, function(err) {
+        console.log(err)
+      });
+    });
+
+    return false;
+  };
+
+  // wrapper for the mailvelope.createDisplayContainer API call
+  this.mailvelope_display_container = function(selector, data, keyring, msgid)
+  {
+    mailvelope.createDisplayContainer(selector, data, keyring, {}).then(function() {
+      $(selector).addClass('mailvelope').find('.message-part, .part-notice').hide();
+      ref.hide_message(msgid);
+      setTimeout(function() { $(window).resize(); }, 10);
+    }, function(err) {
+      console.error(err)
+      ref.hide_message(msgid);
+      ref.display_message('Message decryption failed: ' + err.message, 'error')
+    });
+  };
+
+
   /*********************************************************/
   /*********       mailbox folders methods         *********/
   /*********************************************************/
@@ -3612,6 +3749,10 @@
       );
     }
 
+    if (this.mailvelope_editor) {
+      return this.mailvelope_submit_messageform(draft, saveonly);
+    }
+
     // all checks passed, send message
     var msgid = this.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage'),
       lang = this.spellcheck_lang(),
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index a23bfd6..15e39c3 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -56,6 +56,7 @@
 $messages['contactnameexists'] = 'A contact with the same name already exists.';
 $messages['blockedimages'] = 'To protect your privacy, remote images are blocked in this message.';
 $messages['encryptedmessage'] = 'This is an encrypted message and can not be displayed. Sorry!';
+$messages['externalmessagedecryption'] = 'This is an encrypted message and can be decrypted with your browser extension.';
 $messages['nocontactsfound'] = 'No contacts found.';
 $messages['contactnotfound'] = 'The requested contact was not found.';
 $messages['contactsearchonly'] = 'Enter some search terms to find contacts';
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 684cdf9..a90541d 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1185,7 +1185,23 @@
                 // unsupported (e.g. encrypted)
                 if ($part->realtype) {
                     if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') {
-                        $out .= html::span('part-notice', $RCMAIL->gettext('encryptedmessage'));
+                        if (!empty($_SESSION['browser_caps']['pgpmime']) && $part->realtype == 'multipart/encrypted') {
+                            // find the encrypted message payload part
+                            foreach ($MESSAGE->mime_parts as $mime_id => $mpart) {
+                                if ($mpart->mimetype == 'application/octet-stream' || !empty($mpart->filename)) {
+                                    $out .= html::span('part-notice', $RCMAIL->gettext('externalmessagedecryption'));
+                                    $OUTPUT->set_env('pgp_mime_part', $mime_id);
+                                    $OUTPUT->set_env('pgp_mime_container', '#' . $attrib['id']);
+                                    $OUTPUT->add_label('loadingdata');
+                                    $MESSAGE->encrypted_part = $mime_id;
+                                    break;
+                                }
+                            }
+                        }
+
+                        if (!$MESSAGE->encrypted_part) {
+                            $out .= html::span('part-notice', $RCMAIL->gettext('encryptedmessage'));
+                        }
                     }
                     continue;
                 }
@@ -1219,6 +1235,11 @@
                     rcmail_message_error($MESSAGE->uid);
                 }
 
+                // check if the message body is PGP encrypted
+                if (strpos($body, 'BEGIN PGP MESSAGE') !== false) {
+                    $OUTPUT->set_env('is_pgp_content', '#' . $attrib['id']);
+                }
+
                 $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix',
                     array('part' => $part, 'prefix' => ''));
 
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index effc35f1..9d8ddce 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -881,6 +881,10 @@
 	margin: 8px;
 }
 
+#messagebody.mailvelope > iframe {
+	width: 99% !important;
+}
+
 #message-objects div,
 #messagebody span.part-notice {
 	margin: 8px;
@@ -1298,6 +1302,11 @@
 	bottom: 42px;
 }
 
+#composebodycontainer.mailvelope > iframe[scrolling='no'] {
+	position: relative;
+	top: -12px;
+}
+
 #composebody {
 	position: absolute;
 	top: 0;
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index 95efccf..9841851 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -468,6 +468,14 @@
       $('div.rightcol').hide().attr('aria-hidden', 'true');
       $('div.leftcol').css('margin-right', '0');
     }
+
+    var mvlpe = $('#messagebody.mailvelope');
+    if (mvlpe.length) {
+      var h = $('#messagecontent').length ?
+        $('#messagecontent').height() - 16 :
+        $(window).height() - mvlpe.offset().top - 10;
+      mvlpe.height(h);
+    }
   }
 
 

--
Gitblit v1.9.1