From 0878c846bc2c1030ed01c8db34e20796c31ccd2d Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Sun, 08 Mar 2015 05:54:28 -0400
Subject: [PATCH] Enigma: Larry support, finished PGP decryption, other fixes and improvements

---
 plugins/enigma/enigma.php |  365 +++++++++++++++-------------------------------------
 1 files changed, 105 insertions(+), 260 deletions(-)

diff --git a/plugins/enigma/enigma.php b/plugins/enigma/enigma.php
index 870b923..c190dd6 100644
--- a/plugins/enigma/enigma.php
+++ b/plugins/enigma/enigma.php
@@ -2,7 +2,6 @@
 /*
  +-------------------------------------------------------------------------+
  | 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          |
@@ -33,10 +32,7 @@
     public $rc;
     public $engine;
 
-    private $env_loaded;
-    private $message;
-    private $keys_parts = array();
-    private $keys_bodies = array();
+    private $env_loaded  = false;
 
 
     /**
@@ -44,48 +40,52 @@
      */
     function init()
     {
-        $rcmail = rcmail::get_instance();
-        $this->rc = $rcmail;
-
-        $section = rcube_utils::get_input_value('_section', rcube_utils::INPUT_GET);
+        $this->rc = rcube::get_instance();
 
         if ($this->rc->task == 'mail') {
+            $section = rcube_utils::get_input_value('_section', rcube_utils::INPUT_GET);
+
             // 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'));
+
             // message displaying
-            if ($rcmail->action == 'show' || $rcmail->action == 'preview') {
+            if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
                 $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 ($this->rc->action == 'compose') {
                 $this->load_ui();
                 $this->ui->init($section);
             }
             // message sending (and draft storing)
-            else if ($rcmail->action == 'sendmail') {
+            else if ($this->rc->action == 'sendmail') {
                 //$this->add_hook('outgoing_message_body', array($this, 'msg_encode'));
                 //$this->add_hook('outgoing_message_body', array($this, 'msg_sign'));
             }
+
+            $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('preferences_list', array($this, 'preferences_list'));
-            $this->add_hook('preferences_save', array($this, 'preferences_save'));
+            $this->add_hook('settings_actions', array($this, 'settings_actions'));
+//            $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.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
-            if ($this->rc->action == 'edit-prefs' && preg_match('/^enigma(certs|keys)/', $section)) {
-                $this->load_ui();
-                $this->ui->init($section);
-            }
+            $this->load_ui();
+            $this->ui->add_css();
         }
+
+        $this->add_hook('refresh', array($this, 'refresh'));
     }
 
     /**
@@ -93,8 +93,9 @@
      */
     function load_env()
     {
-        if ($this->env_loaded)
+        if ($this->env_loaded) {
             return;
+        }
 
         $this->env_loaded = true;
 
@@ -113,16 +114,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();
+        }
     }
 
     /**
@@ -130,13 +135,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);
     }
 
     /**
@@ -147,51 +153,59 @@
      *
      * @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/');
+
+        // register as settings action
+        $args['actions'][] = array(
+            'action' => 'plugin.enigmakeys',
+            'class'  => 'enigma keys',
+            'label'  => 'enigmakeys',
+            'title'  => 'enigmakeys',
+            'domain' => 'enigma',
+        );
 /*
-        $p['list']['enigmasettings'] = array(
-            'id' => 'enigmasettings', 'section' => $this->gettext('enigmasettings'),
+        $args['actions'][] = array(
+            'action' => 'plugin.enigmacerts',
+            'class'  => 'enigma certs',
+            'label'  => 'enigmacerts',
+            'title'  => 'enigmacerts',
+            'domain' => 'enigma',
         );
 */
-        $p['list']['enigmacerts'] = array(
-            'id' => 'enigmacerts', 'section' => $this->gettext('enigmacerts'),
-        );
-        $p['list']['enigmakeys'] = array(
-            'id' => 'enigmakeys', 'section' => $this->gettext('enigmakeys'),
-        );
-
-        return $p;
+        return $args;
     }
 
     /**
@@ -209,16 +223,7 @@
             // This makes that section is not removed from the list
             $p['blocks']['dummy']['options']['dummy'] = array();
         }
-        else */
-        if ($p['section'] == 'enigmacerts') {
-            // This makes that section is not removed from the list
-            $p['blocks']['dummy']['options']['dummy'] = array();
-        }
-        else if ($p['section'] == 'enigmakeys') {
-            // This makes that section is not removed from the list
-            $p['blocks']['dummy']['options']['dummy'] = array();
-        }
-
+*/
         return $p;
     }
 
@@ -232,12 +237,13 @@
      */
     function preferences_save($p)
     {
+/*
         if ($p['section'] == 'enigmasettings') {
             $a['prefs'] = array(
-//                'dummy' => rcube_utils::get_input_value('_dummy', rcube_utils::INPUT_POST),
+                'dummy' => rcube_utils::get_input_value('_dummy', rcube_utils::INPUT_POST),
             );
         }
-
+*/
         return $p;
     }
 
@@ -247,6 +253,7 @@
     function preferences_ui()
     {
         $this->load_ui();
+
         $this->ui->init();
     }
 
@@ -262,163 +269,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 = rcube::Q(str_replace('$keyid', enigma_key::format_id($status->getData('id')),
-                        $this->gettext('decryptnokey')));
-                else if ($code == enigma_error::E_BADPASS)
-                    $msg = rcube::Q($this->gettext('decryptbadpass'));
-                else
-                    $msg = rcube::Q($this->gettext('decrypterror'));
-            }
-            else {
-                $attrib['class'] = 'enigmanotice';
-                $msg = rcube::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) {
-                $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
-
-                if ($sig->valid === enigma_error::E_UNVERIFIED) {
-                    $attrib['class'] = 'enigmawarning';
-                    $msg = str_replace('$sender', $sender, $this->gettext('sigunverified'));
-                    $msg = str_replace('$keyid', $sig->id, $msg);
-                    $msg = rcube::Q($msg);
-                }
-                else if ($sig->valid) {
-                    $attrib['class'] = 'enigmanotice';
-                    $msg = rcube::Q(str_replace('$sender', $sender, $this->gettext('sigvalid')));
-                }
-                else {
-                    $attrib['class'] = 'enigmawarning';
-                    $msg = rcube::Q(str_replace('$sender', $sender, $this->gettext('siginvalid')));
-                }
-            }
-            else if ($sig && $sig->getCode() == enigma_error::E_KEYNOTFOUND) {
-                $attrib['class'] = 'enigmawarning';
-                $msg = rcube::Q(str_replace('$keyid', enigma_key::format_id($sig->getData('id')),
-                    $this->gettext('signokey')));
-            }
-            else {
-                $attrib['class'] = 'enigmaerror';
-                $msg = rcube::Q($this->gettext('sigerror'));
-            }
-/*
-            $msg .= '&nbsp;' . html::a(array('href' => "#sigdetails",
-                'onclick' => rcmail_output::JS_OBJECT_NAME.".command('enigma-sig-details')"),
-                rcube::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'];
+        $this->load_ui();
 
-        // 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 $part) {
-            if ($this->is_keys_part($part)) {
-                $this->keys_parts[] = $part->mime_id;
-                $this->keys_bodies[] = $part->mime_id;
-            }
-        }
-        // @TODO: inline PGP keys
-
-        if ($this->keys_parts) {
-            $this->add_texts('localization');
-        }
+        return $this->ui->message_load($p);
     }
 
     /**
@@ -428,34 +292,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 ".rcmail_output::JS_OBJECT_NAME.".enigma_import_attachment('".rcube::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);
     }
 
     /**
@@ -464,22 +303,28 @@
     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();
+    }
+
+    /**
+     * 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