From 46f7b7096450939fe03c95aa81ce06ae4bfca89d Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 28 Mar 2016 06:51:43 -0400
Subject: [PATCH] Enable reply/reply-all/forward buttons also in preview frame of message/rfc822

---
 plugins/enigma/enigma.php |  548 ++++++++++++++++++++++++++++--------------------------
 1 files changed, 282 insertions(+), 266 deletions(-)

diff --git a/plugins/enigma/enigma.php b/plugins/enigma/enigma.php
index a4009ce..8e8ded2 100644
--- a/plugins/enigma/enigma.php
+++ b/plugins/enigma/enigma.php
@@ -1,42 +1,32 @@
 <?php
-/*
+
+/**
  +-------------------------------------------------------------------------+
  | Enigma Plugin for Roundcube                                             |
- | Version 0.1                                                             |
  |                                                                         |
- | This program is free software; you can redistribute it and/or modify    |
- | it under the terms of the GNU General Public License version 2          |
- | as published by the Free Software Foundation.                           |
+ | Copyright (C) 2010-2015 The Roundcube Dev Team                          |
  |                                                                         |
- | This program is distributed in the hope that it will be useful,         |
- | but WITHOUT ANY WARRANTY; without even the implied warranty of          |
- | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           |
- | GNU General Public License for more details.                            |
- |                                                                         |
- | You should have received a copy of the GNU General Public License along |
- | with this program; if not, write to the Free Software Foundation, Inc., |
- | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             |
+ | 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.                       |
  |                                                                         |
  +-------------------------------------------------------------------------+
  | Author: Aleksander Machniak <alec@alec.pl>                              |
  +-------------------------------------------------------------------------+
 */
 
-/*
-    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';
     public $rc;
     public $engine;
+    public $ui;
 
-    private $env_loaded;
-    private $message;
-    private $keys_parts = array();
-    private $keys_bodies = array();
+    private $env_loaded = false;
 
 
     /**
@@ -44,47 +34,59 @@
      */
     function init()
     {
-        $rcmail = rcmail::get_instance();
-        $this->rc = $rcmail;
+        $this->rc = rcube::get_instance();
 
         if ($this->rc->task == 'mail') {
             // message parse/display hooks
-            $this->add_hook('message_part_structure', array($this, 'parse_structure'));
+            $this->add_hook('message_part_structure', array($this, 'part_structure'));
+            $this->add_hook('message_part_body', array($this, 'part_body'));
             $this->add_hook('message_body_prefix', array($this, 'status_message'));
 
+            $this->register_action('plugin.enigmaimport', array($this, 'import_file'));
+
+            // load the Enigma plugin configuration
+            $this->load_config();
+
+            $enabled = $this->rc->config->get('enigma_encryption', true);
+
             // message displaying
-            if ($rcmail->action == 'show' || $rcmail->action == 'preview') {
+            if ($this->rc->action == 'show' || $this->rc->action == 'preview' || $this->rc->action == 'print') {
                 $this->add_hook('message_load', array($this, 'message_load'));
                 $this->add_hook('template_object_messagebody', array($this, 'message_output'));
-                $this->register_action('plugin.enigmaimport', array($this, 'import_file'));
             }
             // message composing
-            else if ($rcmail->action == 'compose') {
+            else if ($enabled && $this->rc->action == 'compose') {
+                $this->add_hook('message_compose_body', array($this, 'message_compose'));
+
                 $this->load_ui();
-                $this->ui->init($section);
+                $this->ui->init();
             }
             // message sending (and draft storing)
-            else if ($rcmail->action == 'sendmail') {
-                //$this->add_hook('outgoing_message_body', array($this, 'msg_encode'));
-                //$this->add_hook('outgoing_message_body', array($this, 'msg_sign'));
+            else if ($enabled && $this->rc->action == 'send') {
+                $this->add_hook('message_ready', array($this, 'message_ready'));
             }
+
+            $this->password_handler();
         }
         else if ($this->rc->task == 'settings') {
             // add hooks for Enigma settings
-            $this->add_hook('preferences_sections_list', array($this, 'preferences_section'));
+            $this->add_hook('settings_actions', array($this, 'settings_actions'));
+            $this->add_hook('preferences_sections_list', array($this, 'preferences_sections_list'));
             $this->add_hook('preferences_list', array($this, 'preferences_list'));
             $this->add_hook('preferences_save', array($this, 'preferences_save'));
 
             // register handler for keys/certs management
-            $this->register_action('plugin.enigma', array($this, 'preferences_ui'));
+            $this->register_action('plugin.enigmakeys', array($this, 'preferences_ui'));
+//            $this->register_action('plugin.enigmacerts', array($this, 'preferences_ui'));
 
-            // grab keys/certs management iframe requests
-            $section = get_input_value('_section', RCUBE_INPUT_GET);
-            if ($this->rc->action == 'edit-prefs' && preg_match('/^enigma(certs|keys)/', $section)) {
-                $this->load_ui();
-                $this->ui->init($section);
+            $this->load_ui();
+
+            if (empty($_REQUEST['_framed']) || strpos($this->rc->action, 'plugin.enigma') === 0) {
+                $this->ui->add_css();
             }
         }
+
+        $this->add_hook('refresh', array($this, 'refresh'));
     }
 
     /**
@@ -92,8 +94,9 @@
      */
     function load_env()
     {
-        if ($this->env_loaded)
+        if ($this->env_loaded) {
             return;
+        }
 
         $this->env_loaded = true;
 
@@ -112,16 +115,20 @@
     /**
      * Plugin UI initialization.
      */
-    function load_ui()
+    function load_ui($all = false)
     {
-        if ($this->ui)
-            return;
+        if (!$this->ui) {
+            // load config/localization
+            $this->load_env();
 
-        // load config/localization
-        $this->load_env();
+            // Load UI
+            $this->ui = new enigma_ui($this, $this->home);
+        }
 
-        // Load UI
-        $this->ui = new enigma_ui($this, $this->home);
+        if ($all) {
+            $this->ui->add_css();
+            $this->ui->add_js();
+        }
     }
 
     /**
@@ -129,13 +136,14 @@
      */
     function load_engine()
     {
-        if ($this->engine)
-            return;
+        if ($this->engine) {
+            return $this->engine;
+        }
 
         // load config/localization
         $this->load_env();
 
-        $this->engine = new enigma_engine($this);
+        return $this->engine = new enigma_engine($this);
     }
 
     /**
@@ -146,47 +154,73 @@
      *
      * @return array Modified parameters
      */
-    function parse_structure($p)
+    function part_structure($p)
     {
-        $struct = $p['structure'];
+        $this->load_engine();
 
-        if ($p['mimetype'] == 'text/plain' || $p['mimetype'] == 'application/pgp') {
-            $this->parse_plain($p);
-        }
-        else if ($p['mimetype'] == 'multipart/signed') {
-            $this->parse_signed($p);
-        }
-        else if ($p['mimetype'] == 'multipart/encrypted') {
-            $this->parse_encrypted($p);
-        }
-        else if ($p['mimetype'] == 'application/pkcs7-mime') {
-            $this->parse_encrypted($p);
-        }
-
-        return $p;
+        return $this->engine->part_structure($p);
     }
 
     /**
-     * Handler for preferences_sections_list hook.
-     * Adds Enigma settings sections into preferences sections list.
+     * Handler for message_part_body hook.
+     * Called to get body of a message part.
      *
      * @param array Original parameters
      *
      * @return array Modified parameters
      */
-    function preferences_section($p)
+    function part_body($p)
+    {
+        $this->load_engine();
+
+        return $this->engine->part_body($p);
+    }
+
+    /**
+     * Handler for settings_actions hook.
+     * Adds Enigma settings section into preferences.
+     *
+     * @param array Original parameters
+     *
+     * @return array Modified parameters
+     */
+    function settings_actions($args)
     {
         // add labels
         $this->add_texts('localization/');
 
-        $p['list']['enigmasettings'] = array(
-            'id' => 'enigmasettings', 'section' => $this->gettext('enigmasettings'),
+        // register as settings action
+        $args['actions'][] = array(
+            'action' => 'plugin.enigmakeys',
+            'class'  => 'enigma keys',
+            'label'  => 'enigmakeys',
+            'title'  => 'enigmakeys',
+            'domain' => 'enigma',
         );
-        $p['list']['enigmacerts'] = array(
-            'id' => 'enigmacerts', 'section' => $this->gettext('enigmacerts'),
+/*
+        $args['actions'][] = array(
+            'action' => 'plugin.enigmacerts',
+            'class'  => 'enigma certs',
+            'label'  => 'enigmacerts',
+            'title'  => 'enigmacerts',
+            'domain' => 'enigma',
         );
-        $p['list']['enigmakeys'] = array(
-            'id' => 'enigmakeys', 'section' => $this->gettext('enigmakeys'),
+*/
+        return $args;
+    }
+
+    /**
+     * Handler for preferences_sections_list hook.
+     * Adds Encryption settings section into preferences sections list.
+     *
+     * @param array Original parameters
+     *
+     * @return array Modified parameters
+     */
+    function preferences_sections_list($p)
+    {
+        $p['list']['enigma'] = array(
+            'id' => 'enigma', 'section' => $this->gettext('encryption'),
         );
 
         return $p;
@@ -202,17 +236,128 @@
      */
     function preferences_list($p)
     {
-        if ($p['section'] == 'enigmasettings') {
-            // This makes that section is not removed from the list
-            $p['blocks']['dummy']['options']['dummy'] = array();
+        if ($p['section'] != 'enigma') {
+            return $p;
         }
-        else if ($p['section'] == 'enigmacerts') {
-            // This makes that section is not removed from the list
-            $p['blocks']['dummy']['options']['dummy'] = array();
+
+        $no_override = array_flip((array)$this->rc->config->get('dont_override'));
+
+        $p['blocks']['main']['name'] = $this->gettext('mainoptions');
+
+        if (!isset($no_override['enigma_encryption'])) {
+            if (!$p['current']) {
+                $p['blocks']['main']['content'] = true;
+                return $p;
+            }
+
+            $field_id = 'rcmfd_enigma_encryption';
+            $input    = new html_checkbox(array(
+                    'name'  => '_enigma_encryption',
+                    'id'    => $field_id,
+                    'value' => 1,
+            ));
+
+            $p['blocks']['main']['options']['enigma_encryption'] = array(
+                'title'   => html::label($field_id, $this->gettext('supportencryption')),
+                'content' => $input->show(intval($this->rc->config->get('enigma_encryption'))),
+            );
         }
-        else if ($p['section'] == 'enigmakeys') {
-            // This makes that section is not removed from the list
-            $p['blocks']['dummy']['options']['dummy'] = array();
+
+        if (!isset($no_override['enigma_signatures'])) {
+            if (!$p['current']) {
+                $p['blocks']['main']['content'] = true;
+                return $p;
+            }
+
+            $field_id = 'rcmfd_enigma_signatures';
+            $input    = new html_checkbox(array(
+                    'name'  => '_enigma_signatures',
+                    'id'    => $field_id,
+                    'value' => 1,
+            ));
+
+            $p['blocks']['main']['options']['enigma_signatures'] = array(
+                'title'   => html::label($field_id, $this->gettext('supportsignatures')),
+                'content' => $input->show(intval($this->rc->config->get('enigma_signatures'))),
+            );
+        }
+
+        if (!isset($no_override['enigma_decryption'])) {
+            if (!$p['current']) {
+                $p['blocks']['main']['content'] = true;
+                return $p;
+            }
+
+            $field_id = 'rcmfd_enigma_decryption';
+            $input    = new html_checkbox(array(
+                    'name'  => '_enigma_decryption',
+                    'id'    => $field_id,
+                    'value' => 1,
+            ));
+
+            $p['blocks']['main']['options']['enigma_decryption'] = array(
+                'title'   => html::label($field_id, $this->gettext('supportdecryption')),
+                'content' => $input->show(intval($this->rc->config->get('enigma_decryption'))),
+            );
+        }
+
+        if (!isset($no_override['enigma_sign_all'])) {
+            if (!$p['current']) {
+                $p['blocks']['main']['content'] = true;
+                return $p;
+            }
+
+            $field_id = 'rcmfd_enigma_sign_all';
+            $input    = new html_checkbox(array(
+                    'name'  => '_enigma_sign_all',
+                    'id'    => $field_id,
+                    'value' => 1,
+            ));
+
+            $p['blocks']['main']['options']['enigma_sign_all'] = array(
+                'title'   => html::label($field_id, $this->gettext('signdefault')),
+                'content' => $input->show($this->rc->config->get('enigma_sign_all') ? 1 : 0),
+            );
+        }
+
+        if (!isset($no_override['enigma_encrypt_all'])) {
+            if (!$p['current']) {
+                $p['blocks']['main']['content'] = true;
+                return $p;
+            }
+
+            $field_id = 'rcmfd_enigma_encrypt_all';
+            $input    = new html_checkbox(array(
+                    'name'  => '_enigma_encrypt_all',
+                    'id'    => $field_id,
+                    'value' => 1,
+            ));
+
+            $p['blocks']['main']['options']['enigma_encrypt_all'] = array(
+                'title'   => html::label($field_id, $this->gettext('encryptdefault')),
+                'content' => $input->show($this->rc->config->get('enigma_encrypt_all') ? 1 : 0),
+            );
+        }
+
+        if (!isset($no_override['enigma_password_time'])) {
+            if (!$p['current']) {
+                $p['blocks']['main']['content'] = true;
+                return $p;
+            }
+
+            $field_id = 'rcmfd_enigma_password_time';
+            $select   = new html_select(array('name' => '_enigma_password_time', 'id' => $field_id));
+
+            foreach (array(1, 5, 10, 15, 30) as $m) {
+                $label = $this->gettext(array('name' => 'nminutes', 'vars' => array('m' => $m)));
+                $select->add($label, $m);
+            }
+            $select->add($this->gettext('wholesession'), 0);
+
+            $p['blocks']['main']['options']['enigma_password_time'] = array(
+                'title'   => html::label($field_id, $this->gettext('passwordtime')),
+                'content' => $select->show(intval($this->rc->config->get('enigma_password_time'))),
+            );
         }
 
         return $p;
@@ -228,9 +373,14 @@
      */
     function preferences_save($p)
     {
-        if ($p['section'] == 'enigmasettings') {
-            $a['prefs'] = array(
-//                'dummy' => get_input_value('_dummy', RCUBE_INPUT_POST),
+        if ($p['section'] == 'enigma') {
+            $p['prefs'] = array(
+                'enigma_signatures'    => (bool) rcube_utils::get_input_value('_enigma_signatures', rcube_utils::INPUT_POST),
+                'enigma_decryption'    => (bool) rcube_utils::get_input_value('_enigma_decryption', rcube_utils::INPUT_POST),
+                'enigma_encryption'    => (bool) rcube_utils::get_input_value('_enigma_encryption', rcube_utils::INPUT_POST),
+                'enigma_sign_all'      => (bool) rcube_utils::get_input_value('_enigma_sign_all', rcube_utils::INPUT_POST),
+                'enigma_encrypt_all'   => (bool) rcube_utils::get_input_value('_enigma_encrypt_all', rcube_utils::INPUT_POST),
+                'enigma_password_time' => intval(rcube_utils::get_input_value('_enigma_password_time', rcube_utils::INPUT_POST)),
             );
         }
 
@@ -243,6 +393,7 @@
     function preferences_ui()
     {
         $this->load_ui();
+
         $this->ui->init();
     }
 
@@ -258,157 +409,20 @@
      */
     function status_message($p)
     {
-        $part_id = $p['part']->mime_id;
+        $this->load_ui();
 
-        // skip: not a message part
-        if ($p['part'] instanceof rcube_message)
-            return $p;
-
-        // skip: message has no signed/encoded content
-        if (!$this->engine)
-            return $p;
-
-        // Decryption status
-        if (isset($this->engine->decryptions[$part_id])) {
-
-            // get decryption status
-            $status = $this->engine->decryptions[$part_id];
-
-            // Load UI and add css script
-            $this->load_ui();
-            $this->ui->add_css();
-
-            // display status info
-            $attrib['id'] = 'enigma-message';
-
-            if ($status instanceof enigma_error) {
-                $attrib['class'] = 'enigmaerror';
-                $code = $status->getCode();
-                if ($code == enigma_error::E_KEYNOTFOUND)
-                    $msg = Q(str_replace('$keyid', enigma_key::format_id($status->getData('id')),
-                        $this->gettext('decryptnokey')));
-                else if ($code == enigma_error::E_BADPASS)
-                    $msg = Q($this->gettext('decryptbadpass'));
-                else
-                    $msg = Q($this->gettext('decrypterror'));
-            }
-            else {
-                $attrib['class'] = 'enigmanotice';
-                $msg = Q($this->gettext('decryptok'));
-            }
-
-            $p['prefix'] .= html::div($attrib, $msg);
-        }
-
-        // Signature verification status
-        if (isset($this->engine->signed_parts[$part_id])
-            && ($sig = $this->engine->signatures[$this->engine->signed_parts[$part_id]])
-        ) {
-            // add css script
-            $this->load_ui();
-            $this->ui->add_css();
-
-            // display status info
-            $attrib['id'] = 'enigma-message';
-
-            if ($sig instanceof enigma_signature) {
-                if ($sig->valid) {
-                    $attrib['class'] = 'enigmanotice';
-                    $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
-                    $msg = Q(str_replace('$sender', $sender, $this->gettext('sigvalid')));
-                }
-                else {
-                    $attrib['class'] = 'enigmawarning';
-                    $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
-                    $msg = Q(str_replace('$sender', $sender, $this->gettext('siginvalid')));
-                }
-            }
-            else if ($sig->getCode() == enigma_error::E_KEYNOTFOUND) {
-                $attrib['class'] = 'enigmawarning';
-                $msg = Q(str_replace('$keyid', enigma_key::format_id($sig->getData('id')),
-                    $this->gettext('signokey')));
-            }
-            else {
-                $attrib['class'] = 'enigmaerror';
-                $msg = Q($this->gettext('sigerror'));
-            }
-/*
-            $msg .= '&nbsp;' . html::a(array('href' => "#sigdetails",
-                'onclick' => JS_OBJECT_NAME.".command('enigma-sig-details')"),
-                Q($this->gettext('showdetails')));
-*/
-            // test
-//            $msg .= '<br /><pre>'.$sig->body.'</pre>';
-
-            $p['prefix'] .= html::div($attrib, $msg);
-
-            // Display each signature message only once
-            unset($this->engine->signatures[$this->engine->signed_parts[$part_id]]);
-        }
-
-        return $p;
+        return $this->ui->status_message($p);
     }
 
-    /**
-     * Handler for plain/text message.
-     *
-     * @param array Reference to hook's parameters (see enigma::parse_structure())
-     */
-    private function parse_plain(&$p)
-    {
-        $this->load_engine();
-        $this->engine->parse_plain($p);
-    }
-    
-    /**
-     * Handler for multipart/signed message.
-     * Verifies signature.
-     *
-     * @param array Reference to hook's parameters (see enigma::parse_structure())
-     */
-    private function parse_signed(&$p)
-    {
-        $this->load_engine();
-        $this->engine->parse_signed($p);
-    }
-
-    /**
-     * Handler for multipart/encrypted and application/pkcs7-mime message.
-     *
-     * @param array Reference to hook's parameters (see enigma::parse_structure())
-     */
-    private function parse_encrypted(&$p)
-    {
-        $this->load_engine();
-        $this->engine->parse_encrypted($p);
-    }
-    
     /**
      * Handler for message_load hook.
      * Check message bodies and attachments for keys/certs.
      */
     function message_load($p)
     {
-        $this->message = $p['object'];
-    
-        // handle attachments vcard attachments
-        foreach ((array)$this->message->attachments as $attachment) {
-            if ($this->is_keys_part($attachment)) {
-                $this->keys_parts[] = $attachment->mime_id;
-            }
-        }
-        // the same with message bodies
-        foreach ((array)$this->message->parts as $idx => $part) {
-            if ($this->is_keys_part($part)) {
-                $this->keys_parts[] = $part->mime_id;
-                $this->keys_bodies[] = $part->mime_id;
-            }
-        }
-        // @TODO: inline PGP keys
+        $this->load_ui();
 
-        if ($this->keys_parts) {
-            $this->add_texts('localization');
-        }
+        return $this->ui->message_load($p);
     }
 
     /**
@@ -418,34 +432,9 @@
      */
     function message_output($p)
     {
-        $attach_script = false;
+        $this->load_ui();
 
-        foreach ($this->keys_parts as $part) {
-
-            // remove part's body
-            if (in_array($part, $this->keys_bodies))
-                $p['content'] = '';
-
-            $style = "margin:0 1em; padding:0.2em 0.5em; border:1px solid #999; width: auto"
-                ." border-radius:4px; -moz-border-radius:4px; -webkit-border-radius:4px";
-
-            // add box below message body
-            $p['content'] .= html::p(array('style' => $style),
-                html::a(array(
-                    'href' => "#",
-                    'onclick' => "return ".JS_OBJECT_NAME.".enigma_import_attachment('".JQ($part)."')",
-                    'title' => $this->gettext('keyattimport')),
-                    html::img(array('src' => $this->url('skins/classic/key_add.png'), 'style' => "vertical-align:middle")))
-                . ' ' . html::span(null, $this->gettext('keyattfound')));
-
-            $attach_script = true;
-        }
-
-        if ($attach_script) {
-            $this->include_script('enigma.js');
-        }
-
-        return $p;
+        return $this->ui->message_output($p);
     }
 
     /**
@@ -454,22 +443,49 @@
     function import_file()
     {
         $this->load_engine();
+
         $this->engine->import_file();
     }
 
     /**
-     * Checks if specified message part is a PGP-key or S/MIME cert data
-     *
-     * @param rcube_message_part Part object
-     *
-     * @return boolean True if part is a key/cert
+     * Handle password submissions
      */
-    private function is_keys_part($part)
+    function password_handler()
     {
-        // @TODO: S/MIME
-        return (
-            // Content-Type: application/pgp-keys
-            $part->mimetype == 'application/pgp-keys'
-        );
+        $this->load_engine();
+
+        $this->engine->password_handler();
+    }
+
+    /**
+     * Handle message_ready hook (encryption/signing)
+     */
+    function message_ready($p)
+    {
+        $this->load_ui();
+
+        return $this->ui->message_ready($p);
+    }
+
+    /**
+     * 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)
+    {
+        // calling enigma_engine constructor to remove passwords
+        // stored in session after expiration time
+        $this->load_engine();
+
+        return $p;
     }
 }

--
Gitblit v1.9.1