From 40d152cfdcace360138ab332e084dfb39a2d2798 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Mon, 01 Jun 2015 06:53:32 -0400
Subject: [PATCH] Make encrypted pgp/mime message composition work

---
 program/steps/mail/compose.inc          |    4 +
 program/localization/en_US/messages.inc |    1 
 program/steps/mail/sendmail.inc         |   49 ++++++++++++++++
 program/js/app.js                       |   90 +++++++++++++++++++++++++----
 4 files changed, 129 insertions(+), 15 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index b43ca00..36694b2 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -3347,17 +3347,17 @@
   this.check_mailvelope = function(action)
   {
     if (typeof window.mailvelope !== 'undefined') {
-      this.mailvelope_init(action);
+      this.mailvelope_load(action);
     }
     else {
       $(window).on('mailvelope', function() {
-        ref.mailvelope_init(action);
+        ref.mailvelope_load(action);
       });
     }
   };
 
   // 
-  this.mailvelope_init = function(action)
+  this.mailvelope_load = function(action)
   {
     if (this.env.browser_capabilities)
       this.env.browser_capabilities['pgpmime'] = 1;
@@ -3366,16 +3366,21 @@
 
     mailvelope.getKeyring(keyring).then(function(kr) {
       ref.mailvelope_keyring = kr;
+      ref.mailvelope_init(action, kr);
     }).catch(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;
+        ref.mailvelope_init(action, kr);
       }).catch(function(err) {
         console.error(err);
       });
     });
+  };
 
+  // 
+  this.mailvelope_init = function(action, keyring)
+  {
     if (action == 'show' || action == 'preview') {
       // decrypt text body
       if (this.env.is_pgp_content && window.mailvelope) {
@@ -3413,17 +3418,22 @@
     // remove Mailvelope editor if active
     if (ref.mailvelope_editor) {
       ref.mailvelope_editor = null;
+      ref.compose_skip_unsavedcheck = false;
       ref.set_button('compose-encrypted', 'act');
+
       container.removeClass('mailvelope')
         .find('iframe:not([aria-hidden=true])').remove();
       $('#' + ref.env.composebody).show();
+      $("[name='_pgpmime']").remove();
     }
     // embed Mailvelope editor container
     else {
       var options = { predefinedText: $('#' + this.env.composebody).val() };
-      mailvelope.createEditorContainer('#' + container.attr('id'), ref.mailvelope_keyring.identifier, options).then(function(editor) {
+      mailvelope.createEditorContainer('#' + container.attr('id'), ref.mailvelope_keyring, options).then(function(editor) {
         ref.mailvelope_editor = editor;
+        ref.compose_skip_unsavedcheck = true;
         ref.set_button('compose-encrypted', 'sel');
+
         container.addClass('mailvelope');
         $('#' + ref.env.composebody).hide();
       }).catch(function(err) {
@@ -3464,19 +3474,65 @@
         return false;
       }
 
-      ref.mailvelope_editor.encrypt(recipients).then(function(armored) {
-        console.log('encrypted message', armored);
-        var form = ref.gui_objects.messageform;
+      // add sender identity to recipients to be able to decrypt our very own message
+      var senders = [], selected_sender = ref.env.identities[$("[name='_from'] option:selected").val()];
+      $.each(ref.env.identities, function(k, sender) {
+        senders.push(sender.email);
+      });
 
-        // all checks passed, send message
-        // var msgid = ref.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage')
+      ref.mailvelope_keyring.validKeyForAddress(senders).then(function(status) {
+        valid_sender = null;
+        $.each(status, function(k,v) {
+          if (v !== false) {
+            valid_sender = k;
+            if (valid_sender == selected_sender) {
+              return false;  // break
+            }
+          }
+        });
+
+        if (!valid_sender) {
+          if (!confirm(ref.get_label('nopubkeyforsender'))) {
+            return false;
+          }
+        }
+
+        recipients.push(valid_sender);
+
+        ref.mailvelope_editor.encrypt(recipients).then(function(armored) {
+          // all checks passed, send message
+          var form = ref.gui_objects.messageform,
+            hidden = $("[name='_pgpmime']", form),
+            msgid = ref.set_busy(true, draft || saveonly ? 'savingmessage' : 'sendingmessage')
+
+          form.target = 'savetarget';
+          form._draft.value = draft ? '1' : '';
+          form.action = ref.add_url(form.action, '_unlock', msgid);
+          form.action = ref.add_url(form.action, '_framed', 1);
+
+          if (saveonly) {
+            form.action = ref.add_url(form.action, '_saveonly', 1);
+          }
+
+          // send pgp conent via hidden field
+          if (!hidden.length) {
+            hidden = $('<input type="hidden" name="_pgpmime">').appendTo(form);
+          }
+          hidden.val(armored);
+
+          form.submit();
+
+        }).catch(function(err) {
+          console.log(err);
+        });  // mailvelope_editor.encrypt()
 
       }).catch(function(err) {
-        console.log(err);
-      });
+        console.error(err);
+      });  // mailvelope_keyring.validKeyForAddress(senders)
+
     }).catch(function(err) {
       console.error(err);
-    });
+    });  // mailvelope_keyring.validKeyForAddress(recipients)
 
     return false;
   };
@@ -3767,6 +3823,7 @@
       );
     }
 
+    // delegate sending to Mailvelope routine
     if (this.mailvelope_editor) {
       return this.mailvelope_submit_messageform(draft, saveonly);
     }
@@ -4088,7 +4145,7 @@
 
       // reset history of hidden iframe used for saving draft (#1489643)
       // but don't do this on timer-triggered draft-autosaving (#1489789)
-      if (window.frames['savetarget'] && window.frames['savetarget'].history && !this.draft_autosave_submit) {
+      if (window.frames['savetarget'] && window.frames['savetarget'].history && !this.draft_autosave_submit && !this.mailvelope_editor) {
         window.frames['savetarget'].history.back();
       }
 
@@ -4158,6 +4215,11 @@
       for (id in this.env.attachments)
         str += id;
 
+    // we can't detect changes in the Mailvelope editor so assume it changed
+    if (this.mailvelope_editor) {
+      str += ';' + new Date().getTime();
+    }
+
     if (save)
       this.cmp_hash = str;
 
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index 2f712f3..be8a0d8 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -58,6 +58,7 @@
 $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['nopubkeyfor'] = 'No valid public key found for $email';
+$messages['nopubkeyforsender'] = 'No valid public key found for your sender identity. Do you want to encrypt the message for the recipients only?';
 $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/compose.inc b/program/steps/mail/compose.inc
index 44f4612..1896e93 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -83,7 +83,7 @@
     'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
     'fileuploaderror', 'sendmessage', 'newresponse', 'responsename', 'responsetext', 'save',
     'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore',
-    'selectimportfile', 'messageissent', 'nopubkeyfor');
+    'selectimportfile', 'messageissent', 'nopubkeyfor', 'nopubkeyforsender');
 
 $OUTPUT->set_pagetitle($RCMAIL->gettext('compose'));
 
@@ -655,6 +655,8 @@
             if (!empty($sql_arr['bcc'])) {
                 $identities[$identity_id]['bcc'] = $sql_arr['bcc'];
             }
+
+            $identities[$identity_id]['email'] = $sql_arr['email'];
         }
 
         $out = $select_from->show($MESSAGE->compose['from']);
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index b3034f5..a766640 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -273,6 +273,18 @@
 // fetch message body
 $message_body = rcube_utils::get_input_value('_message', rcube_utils::INPUT_POST, TRUE, $message_charset);
 
+if (isset($_POST['_pgpmime'])) {
+    $pgp_mime = rcube_utils::get_input_value('_pgpmime', rcube_utils::INPUT_POST);
+    $message_body = 'This is an OpenPGP/MIME encrypted message (RFC 2440 and 3156)';
+    $isHtml = false;
+
+    // clear unencrypted attachments
+    foreach ($COMPOSE['attachments'] as $attach) {
+        $RCMAIL->plugins->exec_hook('attachment_delete', $attach);
+    }
+    $COMPOSE['attachments'] = array();
+}
+
 if ($isHtml) {
     $bstyle = array();
 
@@ -503,6 +515,43 @@
     $text_charset .= ";\r\n format=flowed";
 }
 
+// compose PGP/Mime message
+if ($pgp_mime) {
+    $MAIL_MIME->addAttachment(
+        'Version: 1',
+        'application/pgp-encrypted',
+        'version.txt',  // required by Mail_mime::addAttachment()
+        false,
+        '8bit',
+        '',    // $disposition
+        '',    // $charset
+        '',    // $language
+        '',    // $location
+        null,  // $n_encoding
+        null,  // $f_encoding
+        'PGP/MIME version identification'
+    );
+
+    // patch filename out of the version part
+    foreach ($MAIL_MIME->_parts as $_i => $_part) {
+        if ($_part['c_type'] == 'application/pgp-encrypted') {
+            $MAIL_MIME->_parts[$_i]['name'] = '';
+            break;
+        }
+    }
+
+    $MAIL_MIME->addAttachment(
+        $pgp_mime,
+        'application/octet-stream',
+        'encrypted.asc',
+        false,
+        '8bit',
+        'inline'
+    );
+
+    $MAIL_MIME->setContentType('multipart/encrypted', array('protocol' => "application/pgp-encrypted"));
+}
+
 // encoding settings for mail composing
 $MAIL_MIME->setParam('text_encoding', $transfer_encoding);
 $MAIL_MIME->setParam('html_encoding', 'quoted-printable');

--
Gitblit v1.9.1