From 58c2798fae7749cf7b4aee471a696aed389d0941 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Sun, 07 Jun 2015 11:54:01 -0400
Subject: [PATCH] Implemented password prompt when entering compose page of encrypted message

---
 plugins/enigma/enigma.php                   |   26 +++-
 plugins/enigma/lib/enigma_mime_message.php  |    3 
 plugins/enigma/lib/enigma_driver_gnupg.php  |    3 
 plugins/enigma/lib/enigma_key.php           |    3 
 plugins/enigma/README                       |    3 
 plugins/enigma/lib/enigma_driver.php        |    2 
 plugins/enigma/enigma.js                    |   39 ++++---
 plugins/enigma/lib/enigma_engine.php        |   80 +++++++++------
 plugins/enigma/lib/enigma_subkey.php        |    3 
 plugins/enigma/lib/enigma_error.php         |    3 
 plugins/enigma/lib/enigma_userid.php        |    3 
 plugins/enigma/lib/enigma_ui.php            |   83 +++++++++++++---
 plugins/enigma/lib/enigma_signature.php     |    3 
 plugins/enigma/lib/enigma_driver_phpssl.php |    3 
 14 files changed, 168 insertions(+), 89 deletions(-)

diff --git a/plugins/enigma/README b/plugins/enigma/README
index 0566069..2ccd047 100644
--- a/plugins/enigma/README
+++ b/plugins/enigma/README
@@ -24,9 +24,6 @@
 TODO (must have):
 -----------------
 - Keys export to file
-- Disable Reply/Forward options when viewing encrypted messages
-  until they are decrypted successfully
-- Handling of replying/forwarding of encrypted/signed messages
 - Client-side keys generation (with OpenPGP.js?)
 
 TODO (later):
diff --git a/plugins/enigma/enigma.js b/plugins/enigma/enigma.js
index 4048d8d..21551e3 100644
--- a/plugins/enigma/enigma.js
+++ b/plugins/enigma/enigma.js
@@ -41,10 +41,9 @@
                 e.stopPropagation();
             });
         }
-        else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
-            if (rcmail.env.enigma_password_request) {
-                rcmail.enigma_password_request(rcmail.env.enigma_password_request);
-            }
+
+        if (rcmail.env.enigma_password_request) {
+            rcmail.enigma_password_request(rcmail.env.enigma_password_request);
         }
     }
 });
@@ -324,15 +323,16 @@
             click: function(e) {
                 e.stopPropagation();
 
-                var jq = ref.is_framed() ? window.parent.$ : $,
-                    pass = myprompt_input.val();
+                var jq = ref.is_framed() ? window.parent.$ : $;
 
-                if (!pass) {
+                data.password = myprompt_input.val();
+
+                if (!data.password) {
                     myprompt_input.focus();
                     return;
                 }
 
-                ref.enigma_password_submit(data.key, pass);
+                ref.enigma_password_submit(data);
                 jq(this).remove();
             }
         },
@@ -352,34 +352,37 @@
 }
 
 // submit entered password
-rcube_webmail.prototype.enigma_password_submit = function(keyid, password)
+rcube_webmail.prototype.enigma_password_submit = function(data)
 {
-    if (this.env.action == 'compose') {
-        return this.enigma_password_compose_submit(keyid, password);
+    if (this.env.action == 'compose' && !data['compose-init']) {
+        return this.enigma_password_compose_submit(data);
     }
+
+    var lock = this.set_busy(true, 'loading');
 
     // message preview
     var form = $('<form>').attr({method: 'post', action: location.href, style: 'display:none'})
-        .append($('<input>').attr({type: 'hidden', name: '_keyid', value: keyid}))
-        .append($('<input>').attr({type: 'hidden', name: '_passwd', value: password}))
+        .append($('<input>').attr({type: 'hidden', name: '_keyid', value: data.key}))
+        .append($('<input>').attr({type: 'hidden', name: '_passwd', value: data.password}))
         .append($('<input>').attr({type: 'hidden', name: '_token', value: this.env.request_token}))
+        .append($('<input>').attr({type: 'hidden', name: '_unlock', value: lock}))
         .appendTo(document.body);
 
     form.submit();
 }
 
 // submit entered password - in mail compose page
-rcube_webmail.prototype.enigma_password_compose_submit = function(keyid, password)
+rcube_webmail.prototype.enigma_password_compose_submit = function(data)
 {
     var form = this.gui_objects.messageform;
 
     if (!$('input[name="_keyid"]', form).length) {
-        $(form).append($('<input>').attr({type: 'hidden', name: '_keyid', value: keyid}))
-            .append($('<input>').attr({type: 'hidden', name: '_passwd', value: password}));
+        $(form).append($('<input>').attr({type: 'hidden', name: '_keyid', value: data.key}))
+            .append($('<input>').attr({type: 'hidden', name: '_passwd', value: data.password}));
     }
     else {
-        $('input[name="_keyid"]', form).val(keyid);
-        $('input[name="_passwd"]', form).val(password);
+        $('input[name="_keyid"]', form).val(data.key);
+        $('input[name="_passwd"]', form).val(data.password);
     }
 
     this.submit_messageform(this.env.last_action == 'savedraft');
diff --git a/plugins/enigma/enigma.php b/plugins/enigma/enigma.php
index 3b9aa0b..1ac619c 100644
--- a/plugins/enigma/enigma.php
+++ b/plugins/enigma/enigma.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Enigma Plugin for Roundcube                                             |
  |                                                                         |
@@ -14,11 +15,10 @@
  +-------------------------------------------------------------------------+
 */
 
-/*
-    This class contains only hooks and action handlers.
-    Most plugin logic is placed in enigma_engine and enigma_ui classes.
-*/
-
+/**
+ * This class contains only hooks and action handlers.
+ * Most plugin logic is placed in enigma_engine and enigma_ui classes.
+ */
 class enigma extends rcube_plugin
 {
     public $task = 'mail|settings';
@@ -26,7 +26,7 @@
     public $engine;
     public $ui;
 
-    private $env_loaded  = false;
+    private $env_loaded = false;
 
 
     /**
@@ -51,6 +51,8 @@
             }
             // message composing
             else if ($this->rc->action == 'compose') {
+                $this->add_hook('message_compose_body', array($this, 'message_compose'));
+
                 $this->load_ui();
                 $this->ui->init();
             }
@@ -438,6 +440,16 @@
     }
 
     /**
+     * Handle message_compose_body hook
+     */
+    function message_compose($p)
+    {
+        $this->load_ui();
+
+        return $this->ui->message_compose($p);
+    }
+
+    /**
      * Handler for refresh hook.
      */
     function refresh($p)
diff --git a/plugins/enigma/lib/enigma_driver.php b/plugins/enigma/lib/enigma_driver.php
index 49208b3..4c8340e 100644
--- a/plugins/enigma/lib/enigma_driver.php
+++ b/plugins/enigma/lib/enigma_driver.php
@@ -1,6 +1,6 @@
 <?php
 
-/*
+/**
  +-------------------------------------------------------------------------+
  | Abstract driver for the Enigma Plugin                                   |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_driver_gnupg.php b/plugins/enigma/lib/enigma_driver_gnupg.php
index 52a0ad6..d0b854c 100644
--- a/plugins/enigma/lib/enigma_driver_gnupg.php
+++ b/plugins/enigma/lib/enigma_driver_gnupg.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | GnuPG (PGP) driver for the Enigma Plugin                                |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_driver_phpssl.php b/plugins/enigma/lib/enigma_driver_phpssl.php
index 0250893..a2d73f4 100644
--- a/plugins/enigma/lib/enigma_driver_phpssl.php
+++ b/plugins/enigma/lib/enigma_driver_phpssl.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | S/MIME driver for the Enigma Plugin                                     |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php
index 0111d93..85c2882 100644
--- a/plugins/enigma/lib/enigma_engine.php
+++ b/plugins/enigma/lib/enigma_engine.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Engine of the Enigma Plugin                                             |
  |                                                                         |
@@ -14,12 +15,13 @@
  +-------------------------------------------------------------------------+
 */
 
-/*
-    RFC2440: OpenPGP Message Format
-    RFC3156: MIME Security with OpenPGP
-    RFC3851: S/MIME
-*/
-
+/**
+ * Enigma plugin engine.
+ *
+ * RFC2440: OpenPGP Message Format
+ * RFC3156: MIME Security with OpenPGP
+ * RFC3851: S/MIME
+ */
 class enigma_engine
 {
     private $rc;
@@ -49,7 +51,7 @@
         $this->rc     = rcmail::get_instance();
         $this->enigma = $enigma;
 
-        $this->password_time = $this->rc->config->get('enigma_password_time');
+        $this->password_time = $this->rc->config->get('enigma_password_time') * 60;
 
         // this will remove passwords from session after some time
         if ($this->password_time) {
@@ -485,7 +487,7 @@
         // Store signature data for display
         if (!empty($sig)) {
             $this->signed_parts[$part->mime_id] = $part->mime_id;
-            $this->signatures[$part->mime_id] = $sig;
+            $this->signatures[$part->mime_id]   = $sig;
         }
 
         fclose($fh);
@@ -495,7 +497,7 @@
      * Handler for PGP/MIME signed message.
      * Verifies signature.
      *
-     * @param array  Reference to hook's parameters
+     * @param array Reference to hook's parameters
      */
     private function parse_pgp_signed(&$p)
     {
@@ -503,34 +505,35 @@
             return;
         }
 
-        // Verify signature
-        if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
-            $this->load_pgp_driver();
-            $struct = $p['structure'];
+        if ($this->rc->action != 'show' && $this->rc->action != 'preview') {
+            return;
+        }
 
-            $msg_part = $struct->parts[0];
-            $sig_part = $struct->parts[1];
+        $this->load_pgp_driver();
+        $struct = $p['structure'];
 
-            // Get bodies
-            // Note: The first part body need to be full part body with headers
-            //       it also cannot be decoded
-            $msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
-            $sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
+        $msg_part = $struct->parts[0];
+        $sig_part = $struct->parts[1];
 
-            // Verify
-            $sig = $this->pgp_verify($msg_body, $sig_body);
+        // Get bodies
+        // Note: The first part body need to be full part body with headers
+        //       it also cannot be decoded
+        $msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
+        $sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
 
-            // Store signature data for display
-            $this->signatures[$struct->mime_id] = $sig;
+        // Verify
+        $sig = $this->pgp_verify($msg_body, $sig_body);
 
-            // Message can be multipart (assign signature to each subpart)
-            if (!empty($msg_part->parts)) {
-                foreach ($msg_part->parts as $part)
-                    $this->signed_parts[$part->mime_id] = $struct->mime_id;
-            }
-            else {
-                $this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
-            }
+        // Store signature data for display
+        $this->signatures[$struct->mime_id] = $sig;
+
+        // Message can be multipart (assign signature to each subpart)
+        if (!empty($msg_part->parts)) {
+            foreach ($msg_part->parts as $part)
+                $this->signed_parts[$part->mime_id] = $struct->mime_id;
+        }
+        else {
+            $this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
         }
     }
 
@@ -976,6 +979,9 @@
         $this->rc->output->send();
     }
 
+    /**
+     * Registers password for specified key/cert sent by the password prompt.
+     */
     function password_handler()
     {
         $keyid  = rcube_utils::get_input_value('_keyid', rcube_utils::INPUT_POST);
@@ -986,6 +992,9 @@
         }
     }
 
+    /**
+     * Saves key/cert password in user session
+     */
     function save_password($keyid, $password)
     {
         // we store passwords in session for specified time
@@ -999,6 +1008,9 @@
         $_SESSION['enigma_pass'] = $this->rc->encrypt(serialize($config));
     }
 
+    /**
+     * Returns currently stored passwords
+     */
     function get_passwords()
     {
         if ($config = $_SESSION['enigma_pass']) {
@@ -1011,7 +1023,7 @@
 
         // delete expired passwords
         foreach ((array) $config as $key => $value) {
-            if ($pass_time && $value[1] < $threshold) {
+            if ($threshold && $value[1] < $threshold) {
                 unset($config[$key]);
                 $modified = true;
             }
diff --git a/plugins/enigma/lib/enigma_error.php b/plugins/enigma/lib/enigma_error.php
index 1717a7c..91c281f 100644
--- a/plugins/enigma/lib/enigma_error.php
+++ b/plugins/enigma/lib/enigma_error.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Error class for the Enigma Plugin                                       |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_key.php b/plugins/enigma/lib/enigma_key.php
index 8c61cbd..734cbb8 100644
--- a/plugins/enigma/lib/enigma_key.php
+++ b/plugins/enigma/lib/enigma_key.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Key class for the Enigma Plugin                                         |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_mime_message.php b/plugins/enigma/lib/enigma_mime_message.php
index feed78e..eae8cb7 100644
--- a/plugins/enigma/lib/enigma_mime_message.php
+++ b/plugins/enigma/lib/enigma_mime_message.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Mail_mime wrapper for the Enigma Plugin                                 |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_signature.php b/plugins/enigma/lib/enigma_signature.php
index 2e63a80..4207435 100644
--- a/plugins/enigma/lib/enigma_signature.php
+++ b/plugins/enigma/lib/enigma_signature.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Signature class for the Enigma Plugin                                   |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_subkey.php b/plugins/enigma/lib/enigma_subkey.php
index cd57611..7604473 100644
--- a/plugins/enigma/lib/enigma_subkey.php
+++ b/plugins/enigma/lib/enigma_subkey.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | SubKey class for the Enigma Plugin                                      |
  |                                                                         |
diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php
index e866ba3..c76583e 100644
--- a/plugins/enigma/lib/enigma_ui.php
+++ b/plugins/enigma/lib/enigma_ui.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | User Interface for the Enigma Plugin                                    |
  |                                                                         |
@@ -131,9 +132,10 @@
     /**
      * Initializes key password prompt
      *
-     * @param enigma_error Error object with key info
+     * @param enigma_error $status Error object with key info
+     * @param array        $params Optional prompt parameters
      */
-    function password_prompt($status)
+    function password_prompt($status, $params = array())
     {
         $data = $status->getData('missing');
 
@@ -142,6 +144,10 @@
         }
 
         $data = array('keyid' => key($data), 'user' => $data[key($data)]);
+
+        if (!empty($params)) {
+            $data = array_merge($params, $data);
+        }
 
         if ($this->rc->action == 'send') {
             $this->rc->output->command('enigma_password_request', $data);
@@ -337,23 +343,28 @@
      */
     function tpl_key_data($attrib)
     {
-        $out = '';
+        $out   = '';
         $table = new html_table(array('cols' => 2)); 
 
         // Key user ID
         $table->add('title', $this->enigma->gettext('keyuserid'));
         $table->add(null, rcube::Q($this->data->name));
+
         // Key ID
         $table->add('title', $this->enigma->gettext('keyid'));
         $table->add(null, $this->data->subkeys[0]->get_short_id());
+
         // Key type
         $keytype = $this->data->get_type();
-        if ($keytype == enigma_key::TYPE_KEYPAIR)
+        if ($keytype == enigma_key::TYPE_KEYPAIR) {
             $type = $this->enigma->gettext('typekeypair');
-        else if ($keytype == enigma_key::TYPE_PUBLIC)
+        }
+        else if ($keytype == enigma_key::TYPE_PUBLIC) {
             $type = $this->enigma->gettext('typepublickey');
+        }
         $table->add('title', $this->enigma->gettext('keytype'));
         $table->add(null, $type);
+
         // Key fingerprint
         $table->add('title', $this->enigma->gettext('fingerprint'));
         $table->add(null, $this->data->subkeys[0]->get_fingerprint());
@@ -476,6 +487,9 @@
         $this->rc->output->send();
     }
 
+    /**
+     * Init compose UI (add task button and the menu)
+     */
     private function compose_ui()
     {
         $this->add_css();
@@ -493,12 +507,6 @@
             'height'   => 32
             ), 'toolbar');
 
-        // Options menu contents
-        $this->enigma->add_hook('render_page', array($this, 'compose_menu'));
-    }
-
-    function compose_menu($p)
-    {
         $menu  = new html_table(array('cols' => 2));
         $chbox = new html_checkbox(array('value' => 1));
 
@@ -512,12 +520,10 @@
         $menu->add(null, $chbox->show($this->rc->config->get('enigma_encrypt_all') ? 1 : 0,
             array('name' => '_enigma_encrypt', 'id' => 'enigmaencryptopt')));
 
-        $menu = html::div(array('id' => 'enigmamenu', 'class' => 'popupmenu'),
-            $menu->show());
+        $menu = html::div(array('id' => 'enigmamenu', 'class' => 'popupmenu'), $menu->show());
 
-        $p['content'] .= $menu;
-
-        return $p;
+        // Options menu contents
+        $this->rc->output->add_footer($menu);
     }
 
     /**
@@ -646,7 +652,7 @@
     {
         $engine = $this->enigma->load_engine();
 
-        // handle attachments vcard attachments
+        // handle keys/certs in attachments
         foreach ((array) $p['object']->attachments as $attachment) {
             if ($engine->is_keys_part($attachment)) {
                 $this->keys_parts[] = $attachment->mime_id;
@@ -746,4 +752,45 @@
         return $p;
     }
 
+    /**
+     * Handler for message_compose_body hook
+     * Display error when the message cannot be encrypted
+     * and provide a way to try again with a password.
+     */
+    function message_compose($p)
+    {
+        $engine = $this->enigma->load_engine();
+
+        // skip: message has no signed/encoded content
+        if (!$this->enigma->engine) {
+            return $p;
+        }
+
+        $engine = $this->enigma->engine;
+
+        // Decryption status
+        foreach ($engine->decryptions as $status) {
+            if ($status instanceof enigma_error) {
+                $code = $status->getCode();
+
+                if ($code == enigma_error::E_KEYNOTFOUND) {
+                    $msg = rcube::Q(str_replace('$keyid', enigma_key::format_id($status->getData('id')),
+                        $this->enigma->gettext('decryptnokey')));
+                }
+                else if ($code == enigma_error::E_BADPASS) {
+                    $this->password_prompt($status, array('compose-init' => true));
+                    return $p;
+                }
+                else {
+                    $msg = rcube::Q($this->enigma->gettext('decrypterror'));
+                }
+            }
+        }
+
+        if ($msg) {
+            $this->rc->output->show_message($msg, 'error');
+        }
+
+        return $p;
+    }
 }
diff --git a/plugins/enigma/lib/enigma_userid.php b/plugins/enigma/lib/enigma_userid.php
index da03584..11baef4 100644
--- a/plugins/enigma/lib/enigma_userid.php
+++ b/plugins/enigma/lib/enigma_userid.php
@@ -1,5 +1,6 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | User ID class for the Enigma Plugin                                     |
  |                                                                         |

--
Gitblit v1.9.1