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