Enigma: Implemented messages signing and encrypting

pull/269/head
Aleksander Machniak 9 years ago
parent 0a4e0921d3
commit a99c34159d

@ -6,26 +6,25 @@ WARNING: Don't use with gnupg-2.x!
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 @@ TODO (later):
- 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

@ -12,3 +12,9 @@ $config['enigma_smime_driver'] = 'phpssl';
// 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;

@ -33,6 +33,9 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
}
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,10 +237,46 @@ rcube_webmail.prototype.enigma_add_list_row = function(r)
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 @@ rcube_webmail.prototype.enigma_import_attachment = function(mime_id)
return false;
}
// password request popup
rcube_webmail.prototype.enigma_password_request = function(data)
{
if (!data || !data.keyid) {
@ -268,7 +308,8 @@ rcube_webmail.prototype.enigma_password_request = function(data)
.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 @@ rcube_webmail.prototype.enigma_password_request = function(data)
}
}
// 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 @@ rcube_webmail.prototype.enigma_password_submit = function(keyid, password)
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');
}

@ -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 @@ class enigma extends rcube_plugin
$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 @@ class enigma extends rcube_plugin
// 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 @@ class enigma extends rcube_plugin
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();
@ -208,6 +199,23 @@ class enigma extends rcube_plugin
return $args;
}
/**
* Handler for preferences_sections_list hook.
* Adds Encryption settings section into preferences sections list.
*
* @param array Original parameters
*
* @return array Modified parameters
*/
function preferences_sections_list($p)
{
$p['list']['enigma'] = array(
'id' => 'enigma', 'section' => $this->gettext('encryption'),
);
return $p;
}
/**
* Handler for preferences_list hook.
* Adds options blocks into Enigma settings sections in Preferences.
@ -218,12 +226,52 @@ class enigma extends rcube_plugin
*/
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 @@ class enigma extends rcube_plugin
*/
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,9 +361,20 @@ class enigma extends rcube_plugin
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.
*/

@ -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. |
| |
| 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. |
| Copyright (C) 2010-2015 The Roundcube Dev Team |
| |
| 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 @@ abstract class enigma_driver
/**
* Signing.
*/
abstract function sign($text, $key, $passwd);
abstract function sign($text, $key, $passwd, $mode = null);
/**
* Signature verification.

@ -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> |
@ -30,6 +23,7 @@ class enigma_driver_gnupg extends enigma_driver
private $homedir;
private $user;
function __construct($user)
{
$this->rc = rcmail::get_instance();
@ -86,30 +80,40 @@ class enigma_driver_gnupg extends enigma_driver
}
}
/**
* 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;
}
catch (Exception $e) {
return $this->get_error_from_exception($e);
}
$enc = $this->gpg->encrypt($text);
return $enc;
*/
}
/**
* 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 @@ class enigma_driver_gnupg extends enigma_driver
}
}
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)

@ -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 @@ class enigma_driver_phpssl extends enigma_driver
{
}
function sign($text, $key, $passwd)
function sign($text, $key, $passwd, $mode = null)
{
}

@ -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> |
+-------------------------------------------------------------------------+
*/
/*
@ -41,6 +33,13 @@ class enigma_engine
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;
/**
* Plugin initialization.
@ -124,6 +123,175 @@ class enigma_engine
}
}
/**
* 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;
}
}
/**
* Handler for message_part_structure hook.
* Called for every part of the message.
@ -510,7 +678,6 @@ class enigma_engine
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,7 +700,6 @@ class enigma_engine
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);
@ -553,6 +719,66 @@ class enigma_engine
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();
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 keys listing.
*
@ -576,6 +802,39 @@ class enigma_engine
return $result;
}
/**
* 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.
*

@ -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 @@ class enigma_error
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())

@ -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> |
@ -32,6 +25,9 @@ class enigma_key
const TYPE_KEYPAIR = 1;
const TYPE_PUBLIC = 2;
const CAN_SIGN = 1;
const CAN_ENCRYPT = 2;
/**
* Keys list sorting callback for usort()
*/
@ -91,6 +87,28 @@ class enigma_key
return false;
}
/**
* 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

@ -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;
}
}

@ -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> |

@ -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 @@ class enigma_subkey
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

@ -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 @@ class enigma_ui
*
* @param string Preferences section
*/
function init($section='')
function init()
{
$this->add_js();
@ -150,7 +143,12 @@ class enigma_ui
$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 @@ class enigma_ui
$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 @@ class enigma_ui
$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 @@ class enigma_ui
$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 @@ class enigma_ui
*/
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 @@ class enigma_ui
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 @@ class enigma_ui
// 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 @@ class enigma_ui
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;
}
}

@ -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> |

@ -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['typekeypair'] = 'key pair';
$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['keyrevoke'] = 'Revoke';
$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['decryptok'] = 'Message decrypted.';
$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.';

@ -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';

@ -65,10 +65,15 @@ p.enigmaattachment a {
/***** 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 *****/
#settings-sections .enigma.keys a {
@ -83,6 +88,18 @@ p.enigmaattachment a {
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
{

Loading…
Cancel
Save