From a99c34159d03f2b5b525d6d8cc38509c4ac2f0a1 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Tue, 31 Mar 2015 12:56:32 -0400
Subject: [PATCH] Enigma: Implemented messages signing and encrypting

---
 plugins/enigma/enigma.php                   |  119 ++++-
 plugins/enigma/lib/enigma_mime_message.php  |  299 ++++++++++++++
 plugins/enigma/lib/enigma_driver_gnupg.php  |   62 +-
 plugins/enigma/lib/enigma_key.php           |   40 +
 plugins/enigma/README                       |   17 
 plugins/enigma/lib/enigma_driver.php        |   18 
 plugins/enigma/skins/larry/enigma.css       |   19 
 plugins/enigma/enigma.js                    |   66 +++
 plugins/enigma/lib/enigma_engine.php        |  287 +++++++++++++
 plugins/enigma/lib/enigma_subkey.php        |   17 
 plugins/enigma/config.inc.php.dist          |    6 
 plugins/enigma/lib/enigma_error.php         |   29 
 plugins/enigma/lib/enigma_userid.php        |   15 
 plugins/enigma/lib/enigma_ui.php            |  127 +++--
 plugins/enigma/localization/en_US.inc       |   12 
 plugins/enigma/lib/enigma_signature.php     |   15 
 plugins/enigma/localization/ru_RU.inc       |    1 
 plugins/enigma/lib/enigma_driver_phpssl.php |   19 
 18 files changed, 947 insertions(+), 221 deletions(-)

diff --git a/plugins/enigma/README b/plugins/enigma/README
index 3026b84..7aadbd0 100644
--- a/plugins/enigma/README
+++ b/plugins/enigma/README
@@ -6,26 +6,25 @@
 
 Plugin Status:
 
-+ PGP: signed messages verification
++ PGP: signatures verification
 + PGP: messages decryption
++ PGP: Sending of encrypted/signed messages
 + PGP: keys management UI (keys import and delete)
 + Handling of PGP keys attached to incoming messages
 
 TODO (must have):
 
 - Fix issues with enabled messages_cache
-- PGP: Sending of encrypted/signed messages
-- Per-Identity settings (including keys/certs)
-- Test/Make working with gnupg-2.x
+- PGP: Handling of signed inside encrypted message
+- Make working with gnupg-2.x
 - Keys export to file
 - Disable Reply/Forward options when viewing encrypted messages
   until they are decrypted successfully
-- Handling of replying/forwarding of encrypted messages
+- Handling of replying/forwarding of encrypted/signed messages
 - Add composer.json file
 - Performance improvements:
-   - cache decrypted message key id in cache so we can skip
-     decryption if we have no password in session
-   - cache sig verification status to not verify on every msg preview (optional)
+   - cache decrypted message key id so we can skip decryption if we have no password in session
+   - cache (last or successful only?) sig verification status to not verify on every msg preview (optional)
 
 TODO (later):
 
@@ -45,6 +44,8 @@
 - Mark keys as trusted/untrasted, display appropriate message in verify/decrypt status
 - User-preferences to disable signature verification, decrypting, encrypting or all enigma features
 - Change attachment icon on messages list for encrypted messages (like vcard_attachment plugin does)
+- Support for multi-server installations (store keys in sql database?)
+- Per-Identity settings (including keys/certs)
 
 - S/MIME: Certs generation
 - S/MIME: Certs management
diff --git a/plugins/enigma/config.inc.php.dist b/plugins/enigma/config.inc.php.dist
index 2adb4d9..b58ce8b 100644
--- a/plugins/enigma/config.inc.php.dist
+++ b/plugins/enigma/config.inc.php.dist
@@ -12,3 +12,9 @@
 // Keys directory for all users. Default 'enigma/home'.
 // Must be writeable by PHP process
 $config['enigma_pgp_homedir'] = null;
+
+// Enable signing all messages by default
+$config['enigma_sign_all'] = false;
+
+// Enable encrypting all messages by default
+$config['enigma_encrypt_all'] = false;
diff --git a/plugins/enigma/enigma.js b/plugins/enigma/enigma.js
index a7bc43f..4048d8d 100644
--- a/plugins/enigma/enigma.js
+++ b/plugins/enigma/enigma.js
@@ -33,6 +33,9 @@
     }
     else if (rcmail.env.task == 'mail') {
         if (rcmail.env.action == 'compose') {
+            rcmail.addEventListener('beforesend', function(props) { rcmail.enigma_beforesend_handler(props); })
+                .addEventListener('beforesavedraft', function(props) { rcmail.enigma_beforesavedraft_handler(props); });
+
             $('input,label', $('#enigmamenu')).mouseup(function(e) {
                 // don't close the menu on mouse click inside
                 e.stopPropagation();
@@ -234,9 +237,45 @@
     list.insert_row(row);
 }
 
+
 /*********************************************************/
 /*********        Enigma Message methods         *********/
 /*********************************************************/
+
+// handle message send/save action
+rcube_webmail.prototype.enigma_beforesend_handler = function(props)
+{
+    this.env.last_action = 'send';
+    this.enigma_compose_handler(props);
+}
+
+rcube_webmail.prototype.enigma_beforesavedraft_handler = function(props)
+{
+    this.env.last_action = 'savedraft';
+    this.enigma_compose_handler(props);
+}
+
+rcube_webmail.prototype.enigma_compose_handler = function(props)
+{
+    var form = this.gui_objects.messageform;
+
+    // copy inputs from enigma menu to the form
+    $('#enigmamenu input').each(function() {
+        var id = this.id + '_cpy', input = $('#' + id);
+
+        if (!input.length) {
+            input = $(this).clone();
+            input.prop({id: id, type: 'hidden'}).appendTo(form);
+        }
+
+        input.val(this.checked ? '1' : '');
+    });
+
+    // disable signing when saving drafts
+    if (this.env.last_action == 'savedraft') {
+        $('input[name="_enigma_sign"]', form).val(0);
+    }
+}
 
 // Import attached keys/certs file
 rcube_webmail.prototype.enigma_import_attachment = function(mime_id)
@@ -249,6 +288,7 @@
     return false;
 }
 
+// password request popup
 rcube_webmail.prototype.enigma_password_request = function(data)
 {
     if (!data || !data.keyid) {
@@ -268,7 +308,8 @@
             .appendTo(myprompt);
 
     data.key = data.keyid;
-    data.keyid = data.keyid.substr(0, 8);
+    if (data.keyid.length > 8)
+        data.keyid = data.keyid.substr(data.keyid.length - 8);
 
     $.each(['keyid', 'user'], function() {
         msg = msg.replace('$' + this, data[this]);
@@ -310,8 +351,14 @@
     }
 }
 
+// submit entered password
 rcube_webmail.prototype.enigma_password_submit = function(keyid, password)
 {
+    if (this.env.action == 'compose') {
+        return this.enigma_password_compose_submit(keyid, password);
+    }
+
+    // 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}))
@@ -320,3 +367,20 @@
 
     form.submit();
 }
+
+// submit entered password - in mail compose page
+rcube_webmail.prototype.enigma_password_compose_submit = function(keyid, password)
+{
+    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}));
+    }
+    else {
+        $('input[name="_keyid"]', form).val(keyid);
+        $('input[name="_passwd"]', form).val(password);
+    }
+
+    this.submit_messageform(this.env.last_action == 'savedraft');
+}
diff --git a/plugins/enigma/enigma.php b/plugins/enigma/enigma.php
index c190dd6..0c73290 100644
--- a/plugins/enigma/enigma.php
+++ b/plugins/enigma/enigma.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | Enigma Plugin for Roundcube                                             |
  |                                                                         |
- | 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>                              |
@@ -43,8 +36,6 @@
         $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, 'part_structure'));
             $this->add_hook('message_part_body', array($this, 'part_body'));
@@ -60,12 +51,11 @@
             // message composing
             else if ($this->rc->action == 'compose') {
                 $this->load_ui();
-                $this->ui->init($section);
+                $this->ui->init();
             }
             // message sending (and draft storing)
-            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'));
+            else if ($this->rc->action == 'send') {
+                $this->add_hook('message_ready', array($this, 'message_ready'));
             }
 
             $this->password_handler();
@@ -73,13 +63,14 @@
         else if ($this->rc->task == 'settings') {
             // add hooks for Enigma settings
             $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'));
+            $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'));
+//            $this->register_action('plugin.enigmacerts', array($this, 'preferences_ui'));
 
             $this->load_ui();
             $this->ui->add_css();
@@ -209,6 +200,23 @@
     }
 
     /**
+     * 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;
+    }
+
+    /**
      * Handler for preferences_list hook.
      * Adds options blocks into Enigma settings sections in Preferences.
      *
@@ -218,12 +226,52 @@
      */
     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;
         }
-*/
+
+        $no_override = array_flip((array)$this->rc->config->get('dont_override'));
+
+        $p['blocks']['main']['name'] = $this->gettext('mainoptions');
+
+        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),
+            );
+        }
+
         return $p;
     }
 
@@ -237,13 +285,13 @@
      */
     function preferences_save($p)
     {
-/*
-        if ($p['section'] == 'enigmasettings') {
-            $a['prefs'] = array(
-                'dummy' => rcube_utils::get_input_value('_dummy', rcube_utils::INPUT_POST),
+        if ($p['section'] == 'enigma') {
+            $p['prefs'] = array(
+                'enigma_sign_all'    => intval(rcube_utils::get_input_value('_enigma_sign_all', rcube_utils::INPUT_POST)),
+                'enigma_encrypt_all' => intval(rcube_utils::get_input_value('_enigma_encrypt_all', rcube_utils::INPUT_POST)),
             );
         }
-*/
+
         return $p;
     }
 
@@ -313,10 +361,21 @@
     function password_handler()
     {
         $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);
+    }
+
+    /**
      * Handler for refresh hook.
      */
     function refresh($p)
diff --git a/plugins/enigma/lib/enigma_driver.php b/plugins/enigma/lib/enigma_driver.php
index c0a91ac..49208b3 100644
--- a/plugins/enigma/lib/enigma_driver.php
+++ b/plugins/enigma/lib/enigma_driver.php
@@ -1,20 +1,14 @@
 <?php
+
 /*
  +-------------------------------------------------------------------------+
  | Abstract driver for the Enigma Plugin                                   |
  |                                                                         |
- | 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>                              |
@@ -53,7 +47,7 @@
     /**
      * Signing.
      */
-    abstract function sign($text, $key, $passwd);
+    abstract function sign($text, $key, $passwd, $mode = null);
 
     /**
      * Signature verification.
diff --git a/plugins/enigma/lib/enigma_driver_gnupg.php b/plugins/enigma/lib/enigma_driver_gnupg.php
index 09e23d3..52a0ad6 100644
--- a/plugins/enigma/lib/enigma_driver_gnupg.php
+++ b/plugins/enigma/lib/enigma_driver_gnupg.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | GnuPG (PGP) driver for the Enigma Plugin                                |
  |                                                                         |
- | 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>                              |
@@ -29,6 +22,7 @@
     private $gpg;
     private $homedir;
     private $user;
+
 
     function __construct($user)
     {
@@ -86,30 +80,40 @@
         }
     }
 
+    /**
+     * Encrypt a message
+     *
+     * @param string The message
+     * @param array  List of keys
+     */
     function encrypt($text, $keys)
     {
-/*
-        foreach ($keys as $key) {
-            $this->gpg->addEncryptKey($key);
+        try {
+            foreach ($keys as $key) {
+                $this->gpg->addEncryptKey($key);
+            }
+
+            $dec = $this->gpg->encrypt($text, true);
+            return $dec;
         }
-        $enc = $this->gpg->encrypt($text);
-        return $enc;
-*/
+        catch (Exception $e) {
+            return $this->get_error_from_exception($e);
+        }
     }
 
     /**
-     * Register private keys and passwords
+     * Decrypt a message
      *
      * @param string Encrypted message
      * @param array  List of key-password mapping
      */
     function decrypt($text, $keys = array())
     {
-        foreach ($keys as $key => $password) {
-            $this->gpg->addDecryptKey($key, $password);
-        }
-
         try {
+            foreach ($keys as $key => $password) {
+                $this->gpg->addDecryptKey($key, $password);
+            }
+
             $dec = $this->gpg->decrypt($text);
             return $dec;
         }
@@ -118,13 +122,15 @@
         }
     }
 
-    function sign($text, $key, $passwd)
+    function sign($text, $key, $passwd, $mode = null)
     {
-/*
-        $this->gpg->addSignKey($key, $passwd);
-        $signed = $this->gpg->sign($text, Crypt_GPG::SIGN_MODE_DETACHED);
-        return $signed;
-*/
+        try {
+            $this->gpg->addSignKey($key, $passwd);
+            return $this->gpg->sign($text, $mode, CRYPT_GPG::ARMOR_ASCII, true);
+        }
+        catch (Exception $e) {
+            return $this->get_error_from_exception($e);
+        }
     }
 
     function verify($text, $signature)
diff --git a/plugins/enigma/lib/enigma_driver_phpssl.php b/plugins/enigma/lib/enigma_driver_phpssl.php
index 6686d7d..0250893 100644
--- a/plugins/enigma/lib/enigma_driver_phpssl.php
+++ b/plugins/enigma/lib/enigma_driver_phpssl.php
@@ -1,20 +1,13 @@
 <?php
 /*
  +-------------------------------------------------------------------------+
- | S/MIME driver for the Enigma Plugin                                |
+ | S/MIME driver for the Enigma Plugin                                     |
  |                                                                         |
- | 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>                              |
@@ -81,7 +74,7 @@
     {
     }
 
-    function sign($text, $key, $passwd)
+    function sign($text, $key, $passwd, $mode = null)
     {
     }
 
diff --git a/plugins/enigma/lib/enigma_engine.php b/plugins/enigma/lib/enigma_engine.php
index c3a2e50..9b92994 100644
--- a/plugins/enigma/lib/enigma_engine.php
+++ b/plugins/enigma/lib/enigma_engine.php
@@ -3,23 +3,15 @@
  +-------------------------------------------------------------------------+
  | Engine of the Enigma Plugin                                             |
  |                                                                         |
- | 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>                              |
  +-------------------------------------------------------------------------+
-
 */
 
 /*
@@ -40,6 +32,13 @@
     public $signed_parts = array();
 
     const PASSWORD_TIME = 120;
+
+    const SIGN_MODE_BODY     = 1;
+    const SIGN_MODE_SEPARATE = 2;
+    const SIGN_MODE_MIME     = 3;
+
+    const ENCRYPT_MODE_BODY = 1;
+    const ENCRYPT_MODE_MIME = 2;
 
 
     /**
@@ -121,6 +120,175 @@
                 'file' => __FILE__, 'line' => __LINE__,
                 'message' => "Enigma plugin: ".$result->getMessage()
             ), true, true);
+        }
+    }
+
+    /**
+     * Handler for message signing
+     *
+     * @param Mail_mime Original message
+     * @param int       Encryption mode
+     *
+     * @return enigma_error On error returns error object
+     */
+    function sign_message(&$message, $mode = null)
+    {
+        $mime = new enigma_mime_message($message, enigma_mime_message::PGP_SIGNED);
+        $from = $mime->getFromAddress();
+
+        // find private key
+        $key = $this->find_key($from, true);
+
+        if (empty($key)) {
+            return new enigma_error(enigma_error::E_KEYNOTFOUND);
+        }
+
+        // check if we have password for this key
+        $passwords = $this->get_passwords();
+        $pass      = $passwords[$key->id];
+
+        if ($pass === null) {
+            // ask for password
+            $error = array('missing' => array($key->id => $key->name));
+            return new enigma_error(enigma_error::E_BADPASS, '', $error);
+        }
+
+        // select mode
+        switch ($mode) {
+        case self::SIGN_MODE_BODY:
+            $pgp_mode = Crypt_GPG::SIGN_MODE_CLEAR;
+            break;
+
+        case self::SIGN_MODE_MIME:
+            $pgp_mode = Crypt_GPG::SIGN_MODE_DETACHED;
+            break;
+/*
+        case self::SIGN_MODE_SEPARATE:
+            $pgp_mode = Crypt_GPG::SIGN_MODE_NORMAL;
+            break;
+*/
+        default:
+            if ($mime->isMultipart()) {
+                $pgp_mode = Crypt_GPG::SIGN_MODE_DETACHED;
+            }
+            else {
+                $pgp_mode = Crypt_GPG::SIGN_MODE_CLEAR;
+            }
+        }
+
+        // get message body
+        if ($pgp_mode == Crypt_GPG::SIGN_MODE_CLEAR) {
+            // in this mode we'll replace text part
+            // with the one containing signature
+            $body = $message->getTXTBody();
+        }
+        else {
+            // here we'll build PGP/MIME message
+            $body = $mime->getOrigBody();
+        }
+
+        // sign the body
+        $result = $this->pgp_sign($body, $key->id, $pass, $pgp_mode);
+
+        if ($result !== true) {
+            if ($result->getCode() == enigma_error::E_BADPASS) {
+                // ask for password
+                $error = array('missing' => array($key->id => $key->name));
+                return new enigma_error(enigma_error::E_BADPASS, '', $error);
+            }
+
+            return $result;
+        }
+
+        // replace message body
+        if ($pgp_mode == Crypt_GPG::SIGN_MODE_CLEAR) {
+            $message->setTXTBody($body);
+        }
+        else {
+            $mime->addPGPSignature($body);
+            $message = $mime;
+        }
+    }
+
+    /**
+     * Handler for message encryption
+     *
+     * @param Mail_mime Original message
+     * @param int       Encryption mode
+     * @param bool      Is draft-save action - use only sender's key for encryption
+     *
+     * @return enigma_error On error returns error object
+     */
+    function encrypt_message(&$message, $mode = null, $is_draft = false)
+    {
+        $mime = new enigma_mime_message($message, enigma_mime_message::PGP_ENCRYPTED);
+
+        // always use sender's key
+        $recipients = array($mime->getFromAddress());
+
+        // if it's not a draft we add all recipients' keys
+        if (!$is_draft) {
+            $recipients = array_merge($recipients, $mime->getRecipients());
+        }
+
+        if (empty($recipients)) {
+            return new enigma_error(enigma_error::E_KEYNOTFOUND);
+        }
+
+        $recipients = array_unique($recipients);
+
+        // find recipient public keys
+        foreach ((array) $recipients as $email) {
+            $key = $this->find_key($email);
+
+            if (empty($key)) {
+                return new enigma_error(enigma_error::E_KEYNOTFOUND, '', array(
+                    'missing' => $email
+                ));
+            }
+
+            $keys[] = $key->id;
+        }
+
+        // select mode
+        switch ($mode) {
+        case self::ENCRYPT_MODE_BODY:
+            $encrypt_mode = $mode;
+            break;
+
+        case self::ENCRYPT_MODE_MIME:
+            $encrypt_mode = $mode;
+            break;
+
+        default:
+            $encrypt_mode = $mime->isMultipart() ? self::ENCRYPT_MODE_MIME : self::ENCRYPT_MODE_BODY;
+        }
+
+        // get message body
+        if ($encrypt_mode == self::ENCRYPT_MODE_BODY) {
+            // in this mode we'll replace text part
+            // with the one containing encrypted message
+            $body = $message->getTXTBody();
+        }
+        else {
+            // here we'll build PGP/MIME message
+            $body = $mime->getOrigBody();
+        }
+
+        // sign the body
+        $result = $this->pgp_encrypt($body, $keys);
+
+        if ($result !== true) {
+            return $result;
+        }
+
+        // replace message body
+        if ($encrypt_mode == self::ENCRYPT_MODE_BODY) {
+            $message->setTXTBody($body);
+        }
+        else {
+            $mime->setPGPEncryptedBody($body);
+            $message = $mime;
         }
     }
 
@@ -510,7 +678,6 @@
     private function pgp_verify(&$msg_body, $sig_body=null)
     {
         // @TODO: Handle big bodies using (temp) files
-        // @TODO: caching of verification result
         $sig = $this->pgp_driver->verify($msg_body, $sig_body);
 
         if (($sig instanceof enigma_error) && $sig->getCode() != enigma_error::E_KEYNOTFOUND)
@@ -533,9 +700,68 @@
     private function pgp_decrypt(&$msg_body)
     {
         // @TODO: Handle big bodies using (temp) files
-        // @TODO: caching of verification result
         $keys   = $this->get_passwords();
         $result = $this->pgp_driver->decrypt($msg_body, $keys);
+
+        if ($result instanceof enigma_error) {
+            $err_code = $result->getCode();
+            if (!in_array($err_code, array(enigma_error::E_KEYNOTFOUND, enigma_error::E_BADPASS)))
+                rcube::raise_error(array(
+                    'code' => 600, 'type' => 'php',
+                    'file' => __FILE__, 'line' => __LINE__,
+                    'message' => "Enigma plugin: " . $result->getMessage()
+                    ), true, false);
+            return $result;
+        }
+
+        $msg_body = $result;
+
+        return true;
+    }
+
+    /**
+     * PGP message signing
+     *
+     * @param mixed  Message body
+     * @param string Key ID
+     * @param string Key passphrase
+     * @param int    Signing mode
+     *
+     * @return mixed True or enigma_error
+     */
+    private function pgp_sign(&$msg_body, $keyid, $password, $mode = null)
+    {
+        // @TODO: Handle big bodies using (temp) files
+        $result = $this->pgp_driver->sign($msg_body, $keyid, $password, $mode);
+
+        if ($result instanceof enigma_error) {
+            $err_code = $result->getCode();
+            if (!in_array($err_code, array(enigma_error::E_KEYNOTFOUND, enigma_error::E_BADPASS)))
+                rcube::raise_error(array(
+                    'code' => 600, 'type' => 'php',
+                    'file' => __FILE__, 'line' => __LINE__,
+                    'message' => "Enigma plugin: " . $result->getMessage()
+                    ), true, false);
+            return $result;
+        }
+
+        $msg_body = $result;
+
+        return true;
+    }
+
+    /**
+     * PGP message encrypting
+     *
+     * @param mixed Message body
+     * @param array Keys
+     *
+     * @return mixed True or enigma_error
+     */
+    private function pgp_encrypt(&$msg_body, $keys)
+    {
+        // @TODO: Handle big bodies using (temp) files
+        $result = $this->pgp_driver->encrypt($msg_body, $keys);
 
         if ($result instanceof enigma_error) {
             $err_code = $result->getCode();
@@ -577,6 +803,39 @@
     }
 
     /**
+     * Find PGP private/public key
+     *
+     * @param string E-mail address
+     * @param bool   Need a key for signing?
+     *
+     * @return enigma_key The key
+     */
+    function find_key($email, $can_sign = false)
+    {
+        $this->load_pgp_driver();
+        $result = $this->pgp_driver->list_keys($email);
+
+        if ($result instanceof enigma_error) {
+            rcube::raise_error(array(
+                'code' => 600, 'type' => 'php',
+                'file' => __FILE__, 'line' => __LINE__,
+                'message' => "Enigma plugin: " . $result->getMessage()
+                ), true, false);
+
+            return;
+        }
+
+        $mode = $can_sign ? enigma_key::CAN_SIGN : enigma_key::CAN_ENCRYPT;
+
+        // check key validity and type
+        foreach ($result as $key) {
+            if ($keyid = $key->find_subkey($email, $mode)) {
+                return $key;
+            }
+        }
+    }
+
+    /**
      * PGP key details.
      *
      * @param mixed Key ID
diff --git a/plugins/enigma/lib/enigma_error.php b/plugins/enigma/lib/enigma_error.php
index 7122e8e..1717a7c 100644
--- a/plugins/enigma/lib/enigma_error.php
+++ b/plugins/enigma/lib/enigma_error.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | Error class for the Enigma Plugin                                       |
  |                                                                         |
- | 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>                              |
@@ -28,14 +21,14 @@
     private $data = array();
 
     // error codes
-    const E_OK = 0;
-    const E_INTERNAL = 1;
-    const E_NODATA = 2;
+    const E_OK          = 0;
+    const E_INTERNAL    = 1;
+    const E_NODATA      = 2;
     const E_KEYNOTFOUND = 3;
-    const E_DELKEY = 4;
-    const E_BADPASS = 5;
-    const E_EXPIRED = 6;
-    const E_UNVERIFIED = 7;
+    const E_DELKEY      = 4;
+    const E_BADPASS     = 5;
+    const E_EXPIRED     = 6;
+    const E_UNVERIFIED  = 7;
 
 
     function __construct($code = null, $message = '', $data = array())
diff --git a/plugins/enigma/lib/enigma_key.php b/plugins/enigma/lib/enigma_key.php
index 66670c5..8c61cbd 100644
--- a/plugins/enigma/lib/enigma_key.php
+++ b/plugins/enigma/lib/enigma_key.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | Key class for the Enigma Plugin                                         |
  |                                                                         |
- | 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>                              |
@@ -31,6 +24,9 @@
     const TYPE_UNKNOWN = 0;
     const TYPE_KEYPAIR = 1;
     const TYPE_PUBLIC  = 2;
+
+    const CAN_SIGN    = 1;
+    const CAN_ENCRYPT = 2;
 
     /**
      * Keys list sorting callback for usort()
@@ -92,6 +88,28 @@
     }
 
     /**
+     * Get key ID by user email
+     */
+    function find_subkey($email, $mode)
+    {
+        $now = time();
+
+        foreach ($this->users as $user) {
+            if ($user->email === $email && $user->valid && !$user->revoked) {
+                foreach ($this->subkeys as $subkey) {
+                    if (!$subkey->revoked && (!$subkey->expires || $subkey->expires > $now)) {
+                        if (($mode == self::CAN_ENCRYPT && $subkey->can_encrypt)
+                            || ($mode == self::CAN_SIGN && $subkey->has_private)
+                        ) {
+                            return $subkey;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Converts long ID or Fingerprint to short ID
      * Crypt_GPG uses internal, but e.g. Thunderbird's Enigmail displays short ID
      *
diff --git a/plugins/enigma/lib/enigma_mime_message.php b/plugins/enigma/lib/enigma_mime_message.php
new file mode 100644
index 0000000..feed78e
--- /dev/null
+++ b/plugins/enigma/lib/enigma_mime_message.php
@@ -0,0 +1,299 @@
+<?php
+/*
+ +-------------------------------------------------------------------------+
+ | Mail_mime wrapper for the Enigma Plugin                                 |
+ |                                                                         |
+ | Copyright (C) 2010-2015 The Roundcube Dev Team                          |
+ |                                                                         |
+ | 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>                              |
+ +-------------------------------------------------------------------------+
+*/
+
+class enigma_mime_message extends Mail_mime
+{
+    const PGP_SIGNED    = 1;
+    const PGP_ENCRYPTED = 2;
+
+    protected $_type;
+    protected $_message;
+    protected $_body;
+    protected $_signature;
+    protected $_encrypted;
+
+
+    /**
+     * Object constructor
+     *
+     * @param Mail_mime Original message
+     * @param int       Output message type
+     */
+    function __construct($message, $type)
+    {
+        $this->_message = $message;
+        $this->_type    = $type;
+
+        // clone parameters
+        foreach (array_keys($this->_build_params) as $param) {
+            $this->_build_params[$param] = $message->getParam($param);
+        }
+
+        // clone headers
+        $this->_headers = $message->_headers;
+
+/*
+        if ($message->getParam('delay_file_io')) {
+            // use common temp dir
+            $temp_dir    = $this->config->get('temp_dir');
+            $body_file   = tempnam($temp_dir, 'rcmMsg');
+            $mime_result = $message->saveMessageBody($body_file);
+
+            if (is_a($mime_result, 'PEAR_Error')) {
+                self::raise_error(array('code' => 650, 'type' => 'php',
+                    'file' => __FILE__, 'line' => __LINE__,
+                    'message' => "Could not create message: ".$mime_result->getMessage()),
+                    true, false);
+                return false;
+            }
+
+            $msg_body = fopen($body_file, 'r');
+        }
+        else {
+*/
+            // \r\n is must-have here
+            $this->_body = $message->get() . "\r\n";
+/*
+        }
+*/
+    }
+
+    /**
+     * Check if the message is multipart (requires PGP/MIME)
+     *
+     * @return bool True if it is multipart, otherwise False
+     */
+    function isMultipart()
+    {
+        return $this->_message instanceof enigma_mime_message
+            || !empty($this->_message->_parts) || $this->_message->getHTMLBody();
+    }
+
+    /**
+     * Get e-mail address of message sender
+     *
+     * @return string Sender address
+     */
+    function getFromAddress()
+    {
+        // get sender address
+        $headers = $this->_message->headers();
+        $from    = rcube_mime::decode_address_list($headers['From'], 1, false, null, true);
+        $from    = $from[1];
+
+        return $from;
+    }
+
+    /**
+     * Get recipients' e-mail addresses
+     *
+     * @return array Recipients' addresses
+     */
+    function getRecipients()
+    {
+        // get sender address
+        $headers = $this->_message->headers();
+        $to      = rcube_mime::decode_address_list($headers['To'], null, false, null, true);
+        $cc      = rcube_mime::decode_address_list($headers['Cc'], null, false, null, true);
+        $bcc     = rcube_mime::decode_address_list($headers['Bcc'], null, false, null, true);
+
+        $recipients = array_unique(array_merge($to, $cc, $bcc));
+        $recipients = array_diff($recipients, array('undisclosed-recipients:'));
+
+        return $recipients;
+    }
+
+    /**
+     * Get original message body, to be encrypted/signed
+     *
+     * @return string Message body
+     */
+    function getOrigBody()
+    {
+        $_headers = $this->_message->headers();
+        $headers  = array();
+
+        if ($_headers['Content-Transfer-Encoding']) {
+            $headers[] = 'Content-Transfer-Encoding: ' . $_headers['Content-Transfer-Encoding'];
+        }
+        $headers[] = 'Content-Type: ' . $_headers['Content-Type'];
+
+        return implode("\r\n", $headers) . "\r\n\r\n" . $this->_body;
+    }
+
+    /**
+     * Register signature attachment
+     *
+     * @param string Signature body
+     */
+    function addPGPSignature($body)
+    {
+        $this->_signature = $body;
+    }
+
+    /**
+     * Register encrypted body
+     *
+     * @param string Encrypted body
+     */
+    function setPGPEncryptedBody($body)
+    {
+        $this->_encrypted = $body;
+    }
+
+    /**
+     * Builds the multipart message.
+     *
+     * @param array    $params    Build parameters that change the way the email
+     *                            is built. Should be associative. See $_build_params.
+     * @param resource $filename  Output file where to save the message instead of
+     *                            returning it
+     * @param boolean  $skip_head True if you want to return/save only the message
+     *                            without headers
+     *
+     * @return mixed The MIME message content string, null or PEAR error object
+     * @access public
+     */
+    function get($params = null, $filename = null, $skip_head = false)
+    {
+        if (isset($params)) {
+            while (list($key, $value) = each($params)) {
+                $this->_build_params[$key] = $value;
+            }
+        }
+
+        $this->_checkParams();
+
+        if ($this->_type == self::PGP_SIGNED) {
+            $body   = "This is an OpenPGP/MIME signed message (RFC 4880 and 3156)";
+            $params = array(
+                'content_type' => "multipart/signed; micalg=pgp-sha1; protocol=\"application/pgp-signature\"",
+                'eol'          => $this->_build_params['eol'],
+            );
+
+            $message = new Mail_mimePart($body, $params);
+
+            if (!empty($this->_body)) {
+                $headers = $this->_message->headers();
+                $params  = array('content_type' => $headers['Content-Type']);
+
+                if ($headers['Content-Transfer-Encoding']) {
+                    $params['encoding'] = $headers['Content-Transfer-Encoding'];
+                }
+
+                $message->addSubpart($this->_body, $params);
+            }
+
+            if (!empty($this->_signature)) {
+                $message->addSubpart($this->_signature, array(
+                    'filename'     => 'signature.asc',
+                    'content_type' => 'application/pgp-signature',
+                    'disposition'  => 'attachment',
+                    'description'  => 'OpenPGP digital signature',
+                ));
+            }
+        }
+        else if ($this->_type == self::PGP_ENCRYPTED) {
+            $body   = "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)";
+            $params = array(
+                'content_type' => "multipart/encrypted; protocol=\"application/pgp-encrypted\"",
+                'eol'          => $this->_build_params['eol'],
+            );
+
+            $message = new Mail_mimePart($body, $params);
+
+            $message->addSubpart('Version: 1', array(
+                    'content_type' => 'application/pgp-encrypted',
+                    'description'  => 'PGP/MIME version identification',
+            ));
+
+            $message->addSubpart($this->_encrypted, array(
+                    'content_type' => 'application/octet-stream',
+                    'description'  => 'PGP/MIME encrypted message',
+                    'disposition'  => 'inline',
+                    'filename'     => 'encrypted.asc',
+            ));
+        }
+
+        // Use saved boundary
+        if (!empty($this->_build_params['boundary'])) {
+            $boundary = $this->_build_params['boundary'];
+        }
+        else {
+            $boundary = null;
+        }
+
+        // Write output to file
+        if ($filename) {
+            // Append mimePart message headers and body into file
+            $headers = $message->encodeToFile($filename, $boundary, $skip_head);
+            if ($this->_isError($headers)) {
+                return $headers;
+            }
+            $this->_headers = array_merge($this->_headers, $headers);
+            return null;
+        }
+        else {
+            $output = $message->encode($boundary, $skip_head);
+            if ($this->_isError($output)) {
+                return $output;
+            }
+            $this->_headers = array_merge($this->_headers, $output['headers']);
+            return $output['body'];
+        }
+    }
+
+    /**
+     * Get Content-Type and Content-Transfer-Encoding headers of the message
+     *
+     * @return array Headers array
+     * @access private
+     */
+    function _contentHeaders()
+    {
+        $this->_checkParams();
+
+        $eol = !empty($this->_build_params['eol']) ? $this->_build_params['eol'] : "\r\n";
+
+        // multipart message: and boundary
+        if (!empty($this->_build_params['boundary'])) {
+            $boundary = $this->_build_params['boundary'];
+        }
+        else if (!empty($this->_headers['Content-Type'])
+            && preg_match('/boundary="([^"]+)"/', $this->_headers['Content-Type'], $m)
+        ) {
+            $boundary = $m[1];
+        }
+        else {
+            $boundary = '=_' . md5(rand() . microtime());
+        }
+
+        $this->_build_params['boundary'] = $boundary;
+
+        if ($this->_type == self::PGP_SIGNED) {
+            $headers['Content-Type'] = "multipart/signed; micalg=pgp-sha1;$eol"
+                ." protocol=\"application/pgp-signature\";$eol"
+                ." boundary=\"$boundary\"";
+        }
+        else if ($this->_type == self::PGP_ENCRYPTED) {
+            $headers['Content-Type'] = "multipart/encrypted;$eol"
+                ." protocol=\"application/pgp-encrypted\";$eol"
+                ." boundary=\"$boundary\"";
+        }
+
+        return $headers;
+    }
+}
diff --git a/plugins/enigma/lib/enigma_signature.php b/plugins/enigma/lib/enigma_signature.php
index 6599090..2e63a80 100644
--- a/plugins/enigma/lib/enigma_signature.php
+++ b/plugins/enigma/lib/enigma_signature.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | Signature class for the Enigma Plugin                                   |
  |                                                                         |
- | 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>                              |
diff --git a/plugins/enigma/lib/enigma_subkey.php b/plugins/enigma/lib/enigma_subkey.php
index 1b9fb95..cd57611 100644
--- a/plugins/enigma/lib/enigma_subkey.php
+++ b/plugins/enigma/lib/enigma_subkey.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | SubKey class for the Enigma Plugin                                      |
  |                                                                         |
- | 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>                              |
@@ -31,7 +24,7 @@
     public $has_private;
     public $can_sign;
     public $can_encrypt;
-    
+
     /**
      * Converts internal ID to short ID
      * Crypt_GPG uses internal, but e.g. Thunderbird's Enigmail displays short ID
diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php
index 26396f1..e866ba3 100644
--- a/plugins/enigma/lib/enigma_ui.php
+++ b/plugins/enigma/lib/enigma_ui.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | User Interface for the Enigma Plugin                                    |
  |                                                                         |
- | 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>                              |
@@ -45,7 +38,7 @@
      *
      * @param string Preferences section
      */
-    function init($section='')
+    function init()
     {
         $this->add_js();
 
@@ -150,7 +143,12 @@
 
         $data = array('keyid' => key($data), 'user' => $data[key($data)]);
 
-        $this->rc->output->set_env('enigma_password_request', $data);
+        if ($this->rc->action == 'send') {
+            $this->rc->output->command('enigma_password_request', $data);
+        }
+        else {
+            $this->rc->output->set_env('enigma_password_request', $data);
+        }
 
         // add some labels to client
         $this->rc->output->add_label('enigma.enterkeypasstitle', 'enigma.enterkeypass',
@@ -176,7 +174,7 @@
         $attrib['name'] = $attrib['id'];
 
         $this->rc->output->set_env('contentframe', $attrib['name']);
-        $this->rc->output->set_env('blankpage', $attrib['src'] ? 
+        $this->rc->output->set_env('blankpage', $attrib['src'] ?
             $this->rc->output->abs_url($attrib['src']) : 'program/resources/blank.gif');
 
         return $this->rc->output->frame($attrib);
@@ -223,9 +221,6 @@
         $page     = max(intval(rcube_utils::get_input_value('_p', rcube_utils::INPUT_GPC)), 1);
         $search   = rcube_utils::get_input_value('_q', rcube_utils::INPUT_GPC);
 
-        // define list of cols to be displayed
-//        $a_show_cols = array('name');
-
         // Get the list
         $list = $this->enigma->engine->list_keys($search);
 
@@ -233,24 +228,21 @@
             $this->rc->output->show_message('enigma.keylisterror', 'error');
         else if (empty($list))
             $this->rc->output->show_message('enigma.nokeysfound', 'notice');
-        else {
-            if (is_array($list)) {
-                // Save the size
-                $listsize = count($list);
+        else if (is_array($list)) {
+            // Save the size
+            $listsize = count($list);
 
-                // Sort the list by key (user) name
-                usort($list, array('enigma_key', 'cmp'));
+            // Sort the list by key (user) name
+            usort($list, array('enigma_key', 'cmp'));
 
-                // Slice current page
-                $list = array_slice($list, ($page - 1) * $pagesize, $pagesize);
+            // Slice current page
+            $list = array_slice($list, ($page - 1) * $pagesize, $pagesize);
+            $size = count($list);
 
-                $size = count($list);
-
-                // Add rows
-                foreach ($list as $key) {
-                    $this->rc->output->command('enigma_add_list_row',
-                        array('name' => rcube::Q($key->name), 'id' => $key->id));
-                }
+            // Add rows
+            foreach ($list as $key) {
+                $this->rc->output->command('enigma_add_list_row',
+                    array('name' => rcube::Q($key->name), 'id' => $key->id));
             }
         }
 
@@ -285,11 +277,12 @@
      */
     private function get_rowcount_text($all=0, $curr_count=0, $page=1)
     {
-        if (!$curr_count)
+        if (!$curr_count) {
             $out = $this->enigma->gettext('nokeysfound');
+        }
         else {
             $pagesize = $this->rc->config->get('pagesize', 100);
-            $first = ($page - 1) * $pagesize;
+            $first    = ($page - 1) * $pagesize;
 
             $out = $this->enigma->gettext(array(
                 'name' => 'keysfromto',
@@ -485,18 +478,16 @@
 
     private function compose_ui()
     {
-/*
         $this->add_css();
 
         // Options menu button
-        // @TODO: make this work with non-default skins
         $this->enigma->add_button(array(
             'type'     => 'link',
             'command'  => 'plugin.enigma',
             'onclick'  => "rcmail.command('menu-open', 'enigmamenu', event.target, event)",
             'class'    => 'button enigma',
-            'title'    => 'securityoptions',
-            'label'    => 'securityoptions',
+            'title'    => 'encryptionoptions',
+            'label'    => 'encryption',
             'domain'   => $this->enigma->ID,
             'width'    => 32,
             'height'   => 32
@@ -504,30 +495,27 @@
 
         // Options menu contents
         $this->enigma->add_hook('render_page', array($this, 'compose_menu'));
-*/
     }
 
     function compose_menu($p)
     {
-        $menu = new html_table(array('cols' => 2));
+        $menu  = new html_table(array('cols' => 2));
         $chbox = new html_checkbox(array('value' => 1));
-
-        $menu->add(null, html::label(array('for' => 'enigmadefaultopt'),
-            rcube::Q($this->enigma->gettext('identdefault'))));
-        $menu->add(null, $chbox->show(1, array('name' => '_enigma_default', 'id' => 'enigmadefaultopt')));
 
         $menu->add(null, html::label(array('for' => 'enigmasignopt'),
             rcube::Q($this->enigma->gettext('signmsg'))));
-        $menu->add(null, $chbox->show(1, array('name' => '_enigma_sign', 'id' => 'enigmasignopt')));
+        $menu->add(null, $chbox->show($this->rc->config->get('enigma_sign_all') ? 1 : 0,
+            array('name' => '_enigma_sign', 'id' => 'enigmasignopt')));
 
-        $menu->add(null, html::label(array('for' => 'enigmacryptopt'),
+        $menu->add(null, html::label(array('for' => 'enigmaencryptopt'),
             rcube::Q($this->enigma->gettext('encryptmsg'))));
-        $menu->add(null, $chbox->show(1, array('name' => '_enigma_crypt', 'id' => 'enigmacryptopt')));
+        $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());
 
-        $p['content'] = preg_replace('/(<form name="form"[^>]+>)/i', '\\1'."\n$menu", $p['content']);
+        $p['content'] .= $menu;
 
         return $p;
     }
@@ -715,4 +703,47 @@
         return $p;
     }
 
+    /**
+     * Handle message_ready hook (encryption/signing)
+     */
+    function message_ready($p)
+    {
+        $savedraft = !empty($_POST['_draft']) && empty($_GET['_saveonly']);
+
+        if (!$savedraft && rcube_utils::get_input_value('_enigma_sign', rcube_utils::INPUT_POST)) {
+            $this->enigma->load_engine();
+            $status = $this->enigma->engine->sign_message($p['message']);
+            $mode   = 'sign';
+        }
+
+        if ((!$status instanceof enigma_error) && rcube_utils::get_input_value('_enigma_encrypt', rcube_utils::INPUT_POST)) {
+            $this->enigma->load_engine();
+            $status = $this->enigma->engine->encrypt_message($p['message'], null, $savedraft);
+            $mode   = 'encrypt';
+        }
+
+        if ($mode && ($status instanceof enigma_error)) {
+            $code = $status->getCode();
+
+            if ($code == enigma_error::E_KEYNOTFOUND) {
+                $vars = array('email' => $status->getData('missing'));
+                $msg  = 'enigma.' . $mode . 'nokey';
+            }
+            else if ($code == enigma_error::E_BADPASS) {
+                $msg  = 'enigma.' . $mode . 'badpass';
+                $type = 'warning';
+
+                $this->password_prompt($status);
+            }
+            else {
+                $msg = 'enigma.' . $mode . 'error';
+            }
+
+            $this->rc->output->show_message($msg, $type ?: 'error', $vars);
+            $this->rc->output->send('iframe');
+        }
+
+        return $p;
+    }
+
 }
diff --git a/plugins/enigma/lib/enigma_userid.php b/plugins/enigma/lib/enigma_userid.php
index 36185e7..da03584 100644
--- a/plugins/enigma/lib/enigma_userid.php
+++ b/plugins/enigma/lib/enigma_userid.php
@@ -3,18 +3,11 @@
  +-------------------------------------------------------------------------+
  | User ID class for the Enigma Plugin                                     |
  |                                                                         |
- | 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>                              |
diff --git a/plugins/enigma/localization/en_US.inc b/plugins/enigma/localization/en_US.inc
index 5e8889f..d8e80a8 100644
--- a/plugins/enigma/localization/en_US.inc
+++ b/plugins/enigma/localization/en_US.inc
@@ -1,7 +1,7 @@
 <?php
 
 $labels = array();
-$labels['enigma'] = 'Enigma';
+$labels['encryption'] = 'Encryption';
 $labels['enigmacerts'] = 'S/MIME Certificates';
 $labels['enigmakeys'] = 'PGP Keys';
 $labels['keysfromto'] = 'Keys $from to $to of $count';
@@ -18,6 +18,9 @@
 $labels['keyattfound'] = 'This message contains attached PGP key(s).';
 $labels['keyattimport'] = 'Import key(s)';
 
+$labels['signdefault'] = 'Sign all messages by default';
+$labels['encryptdefault'] = 'Encrypt all messages by default';
+
 $labels['createkeys'] = 'Create a new key pair';
 $labels['importkeys'] = 'Import key(s)';
 $labels['exportkeys'] = 'Export key(s)';
@@ -28,7 +31,7 @@
 $labels['keysend'] = 'Send public key in a message';
 $labels['keychpass'] = 'Change password';
 
-$labels['securityoptions'] = 'Message security options...';
+$labels['encryptionoptions'] = 'Encryption options...';
 $labels['identdefault'] = 'Use settings of selected identity';
 $labels['encryptmsg'] = 'Encrypt this message';
 $labels['signmsg'] = 'Digitally sign this message';
@@ -46,6 +49,11 @@
 $messages['decrypterror'] = 'Decryption failed.';
 $messages['decryptnokey'] = 'Decryption failed. Private key not found. Key ID: $keyid.';
 $messages['decryptbadpass'] = 'Decryption failed. Bad password.';
+$messages['signerror'] = 'Signing failed.';
+$messages['signnokey'] = 'Signing failed. Private key not found.';
+$messages['signbadpass'] = 'Signing failed. Bad password.';
+$messages['encrypterror'] = 'Encryption failed.';
+$messages['encryptnokey'] = 'Encryption failed. Public key not found for $email.';
 $messages['nokeysfound'] = 'No keys found';
 $messages['keyopenerror'] = 'Unable to get key information! Internal error.';
 $messages['keylisterror'] = 'Unable to list keys! Internal error.';
diff --git a/plugins/enigma/localization/ru_RU.inc b/plugins/enigma/localization/ru_RU.inc
index 6859991..e8240de 100644
--- a/plugins/enigma/localization/ru_RU.inc
+++ b/plugins/enigma/localization/ru_RU.inc
@@ -16,7 +16,6 @@
 @version 2010-12-23
 
 */
-$labels['enigma'] = 'Enigma';
 $labels['enigmacerts'] = 'Enigma: Сертификаты (S/MIME)';
 $labels['enigmakeys'] = 'Enigma: Ключи (PGP)';
 $labels['keysfromto'] = 'Ключи от $from к $to в количестве $count';
diff --git a/plugins/enigma/skins/larry/enigma.css b/plugins/enigma/skins/larry/enigma.css
index 046697c..0f393a0 100644
--- a/plugins/enigma/skins/larry/enigma.css
+++ b/plugins/enigma/skins/larry/enigma.css
@@ -65,9 +65,14 @@
 /***** E-mail Compose Page *****/
 
 #messagetoolbar a.button.enigma {
-  text-indent: -5000px;
   background: url(enigma_icons.png) center -122px no-repeat;
 }
+
+#enigmamenu {
+  color: white;
+  padding: 2px 5px;
+}
+
 
 /***** Keys/Certs Management *****/
 
@@ -83,6 +88,18 @@
   background-repeat: no-repeat;
 }
 
+#sections-table #rcmrowenigma .section {
+  background-image: url(enigma_icons.png);
+  background-position: 5px -297px;
+  background-repeat: no-repeat;
+}
+
+#sections-table #rcmrowenigma.selected .section {
+  background-image: url(enigma_icons.png);
+  background-position: 5px -321px;
+  background-repeat: no-repeat;
+}
+
 #mainscreen.enigma #settings-sections,
 #mainscreen.enigma #settings-right
 {

--
Gitblit v1.9.1