Implemented password prompt when entering compose page of encrypted message

pull/281/head
Aleksander Machniak 9 years ago
parent a958748947
commit 58c2798fae

@ -24,9 +24,6 @@ Implemented features:
TODO (must have):
-----------------
- Keys export to file
- Disable Reply/Forward options when viewing encrypted messages
until they are decrypted successfully
- Handling of replying/forwarding of encrypted/signed messages
- Client-side keys generation (with OpenPGP.js?)
TODO (later):

@ -41,10 +41,9 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
e.stopPropagation();
});
}
else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
if (rcmail.env.enigma_password_request) {
rcmail.enigma_password_request(rcmail.env.enigma_password_request);
}
if (rcmail.env.enigma_password_request) {
rcmail.enigma_password_request(rcmail.env.enigma_password_request);
}
}
});
@ -324,15 +323,16 @@ rcube_webmail.prototype.enigma_password_request = function(data)
click: function(e) {
e.stopPropagation();
var jq = ref.is_framed() ? window.parent.$ : $,
pass = myprompt_input.val();
var jq = ref.is_framed() ? window.parent.$ : $;
if (!pass) {
data.password = myprompt_input.val();
if (!data.password) {
myprompt_input.focus();
return;
}
ref.enigma_password_submit(data.key, pass);
ref.enigma_password_submit(data);
jq(this).remove();
}
},
@ -352,34 +352,37 @@ rcube_webmail.prototype.enigma_password_request = function(data)
}
// submit entered password
rcube_webmail.prototype.enigma_password_submit = function(keyid, password)
rcube_webmail.prototype.enigma_password_submit = function(data)
{
if (this.env.action == 'compose') {
return this.enigma_password_compose_submit(keyid, password);
if (this.env.action == 'compose' && !data['compose-init']) {
return this.enigma_password_compose_submit(data);
}
var lock = this.set_busy(true, 'loading');
// 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}))
.append($('<input>').attr({type: 'hidden', name: '_keyid', value: data.key}))
.append($('<input>').attr({type: 'hidden', name: '_passwd', value: data.password}))
.append($('<input>').attr({type: 'hidden', name: '_token', value: this.env.request_token}))
.append($('<input>').attr({type: 'hidden', name: '_unlock', value: lock}))
.appendTo(document.body);
form.submit();
}
// submit entered password - in mail compose page
rcube_webmail.prototype.enigma_password_compose_submit = function(keyid, password)
rcube_webmail.prototype.enigma_password_compose_submit = function(data)
{
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}));
$(form).append($('<input>').attr({type: 'hidden', name: '_keyid', value: data.key}))
.append($('<input>').attr({type: 'hidden', name: '_passwd', value: data.password}));
}
else {
$('input[name="_keyid"]', form).val(keyid);
$('input[name="_passwd"]', form).val(password);
$('input[name="_keyid"]', form).val(data.key);
$('input[name="_passwd"]', form).val(data.password);
}
this.submit_messageform(this.env.last_action == 'savedraft');

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Enigma Plugin for Roundcube |
| |
@ -14,11 +15,10 @@
+-------------------------------------------------------------------------+
*/
/*
This class contains only hooks and action handlers.
Most plugin logic is placed in enigma_engine and enigma_ui classes.
*/
/**
* This class contains only hooks and action handlers.
* Most plugin logic is placed in enigma_engine and enigma_ui classes.
*/
class enigma extends rcube_plugin
{
public $task = 'mail|settings';
@ -26,7 +26,7 @@ class enigma extends rcube_plugin
public $engine;
public $ui;
private $env_loaded = false;
private $env_loaded = false;
/**
@ -51,6 +51,8 @@ class enigma extends rcube_plugin
}
// message composing
else if ($this->rc->action == 'compose') {
$this->add_hook('message_compose_body', array($this, 'message_compose'));
$this->load_ui();
$this->ui->init();
}
@ -437,6 +439,16 @@ class enigma extends rcube_plugin
return $this->ui->message_ready($p);
}
/**
* Handle message_compose_body hook
*/
function message_compose($p)
{
$this->load_ui();
return $this->ui->message_compose($p);
}
/**
* Handler for refresh hook.
*/

@ -1,6 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Abstract driver for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| GnuPG (PGP) driver for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| S/MIME driver for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Engine of the Enigma Plugin |
| |
@ -14,12 +15,13 @@
+-------------------------------------------------------------------------+
*/
/*
RFC2440: OpenPGP Message Format
RFC3156: MIME Security with OpenPGP
RFC3851: S/MIME
*/
/**
* Enigma plugin engine.
*
* RFC2440: OpenPGP Message Format
* RFC3156: MIME Security with OpenPGP
* RFC3851: S/MIME
*/
class enigma_engine
{
private $rc;
@ -49,7 +51,7 @@ class enigma_engine
$this->rc = rcmail::get_instance();
$this->enigma = $enigma;
$this->password_time = $this->rc->config->get('enigma_password_time');
$this->password_time = $this->rc->config->get('enigma_password_time') * 60;
// this will remove passwords from session after some time
if ($this->password_time) {
@ -485,7 +487,7 @@ class enigma_engine
// Store signature data for display
if (!empty($sig)) {
$this->signed_parts[$part->mime_id] = $part->mime_id;
$this->signatures[$part->mime_id] = $sig;
$this->signatures[$part->mime_id] = $sig;
}
fclose($fh);
@ -495,7 +497,7 @@ class enigma_engine
* Handler for PGP/MIME signed message.
* Verifies signature.
*
* @param array Reference to hook's parameters
* @param array Reference to hook's parameters
*/
private function parse_pgp_signed(&$p)
{
@ -503,34 +505,35 @@ class enigma_engine
return;
}
// Verify signature
if ($this->rc->action == 'show' || $this->rc->action == 'preview') {
$this->load_pgp_driver();
$struct = $p['structure'];
if ($this->rc->action != 'show' && $this->rc->action != 'preview') {
return;
}
$msg_part = $struct->parts[0];
$sig_part = $struct->parts[1];
$this->load_pgp_driver();
$struct = $p['structure'];
// Get bodies
// Note: The first part body need to be full part body with headers
// it also cannot be decoded
$msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
$sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
$msg_part = $struct->parts[0];
$sig_part = $struct->parts[1];
// Verify
$sig = $this->pgp_verify($msg_body, $sig_body);
// Get bodies
// Note: The first part body need to be full part body with headers
// it also cannot be decoded
$msg_body = $this->get_part_body($p['object'], $msg_part->mime_id, true);
$sig_body = $this->get_part_body($p['object'], $sig_part->mime_id);
// Store signature data for display
$this->signatures[$struct->mime_id] = $sig;
// Verify
$sig = $this->pgp_verify($msg_body, $sig_body);
// Message can be multipart (assign signature to each subpart)
if (!empty($msg_part->parts)) {
foreach ($msg_part->parts as $part)
$this->signed_parts[$part->mime_id] = $struct->mime_id;
}
else {
$this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
}
// Store signature data for display
$this->signatures[$struct->mime_id] = $sig;
// Message can be multipart (assign signature to each subpart)
if (!empty($msg_part->parts)) {
foreach ($msg_part->parts as $part)
$this->signed_parts[$part->mime_id] = $struct->mime_id;
}
else {
$this->signed_parts[$msg_part->mime_id] = $struct->mime_id;
}
}
@ -976,6 +979,9 @@ class enigma_engine
$this->rc->output->send();
}
/**
* Registers password for specified key/cert sent by the password prompt.
*/
function password_handler()
{
$keyid = rcube_utils::get_input_value('_keyid', rcube_utils::INPUT_POST);
@ -986,6 +992,9 @@ class enigma_engine
}
}
/**
* Saves key/cert password in user session
*/
function save_password($keyid, $password)
{
// we store passwords in session for specified time
@ -999,6 +1008,9 @@ class enigma_engine
$_SESSION['enigma_pass'] = $this->rc->encrypt(serialize($config));
}
/**
* Returns currently stored passwords
*/
function get_passwords()
{
if ($config = $_SESSION['enigma_pass']) {
@ -1011,7 +1023,7 @@ class enigma_engine
// delete expired passwords
foreach ((array) $config as $key => $value) {
if ($pass_time && $value[1] < $threshold) {
if ($threshold && $value[1] < $threshold) {
unset($config[$key]);
$modified = true;
}

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Error class for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Key class for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Mail_mime wrapper for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| Signature class for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| SubKey class for the Enigma Plugin |
| |

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| User Interface for the Enigma Plugin |
| |
@ -131,9 +132,10 @@ class enigma_ui
/**
* Initializes key password prompt
*
* @param enigma_error Error object with key info
* @param enigma_error $status Error object with key info
* @param array $params Optional prompt parameters
*/
function password_prompt($status)
function password_prompt($status, $params = array())
{
$data = $status->getData('missing');
@ -143,6 +145,10 @@ class enigma_ui
$data = array('keyid' => key($data), 'user' => $data[key($data)]);
if (!empty($params)) {
$data = array_merge($params, $data);
}
if ($this->rc->action == 'send') {
$this->rc->output->command('enigma_password_request', $data);
}
@ -337,23 +343,28 @@ class enigma_ui
*/
function tpl_key_data($attrib)
{
$out = '';
$out = '';
$table = new html_table(array('cols' => 2));
// Key user ID
$table->add('title', $this->enigma->gettext('keyuserid'));
$table->add(null, rcube::Q($this->data->name));
// Key ID
$table->add('title', $this->enigma->gettext('keyid'));
$table->add(null, $this->data->subkeys[0]->get_short_id());
// Key type
$keytype = $this->data->get_type();
if ($keytype == enigma_key::TYPE_KEYPAIR)
if ($keytype == enigma_key::TYPE_KEYPAIR) {
$type = $this->enigma->gettext('typekeypair');
else if ($keytype == enigma_key::TYPE_PUBLIC)
}
else if ($keytype == enigma_key::TYPE_PUBLIC) {
$type = $this->enigma->gettext('typepublickey');
}
$table->add('title', $this->enigma->gettext('keytype'));
$table->add(null, $type);
// Key fingerprint
$table->add('title', $this->enigma->gettext('fingerprint'));
$table->add(null, $this->data->subkeys[0]->get_fingerprint());
@ -476,6 +487,9 @@ class enigma_ui
$this->rc->output->send();
}
/**
* Init compose UI (add task button and the menu)
*/
private function compose_ui()
{
$this->add_css();
@ -493,12 +507,6 @@ class enigma_ui
'height' => 32
), 'toolbar');
// Options menu contents
$this->enigma->add_hook('render_page', array($this, 'compose_menu'));
}
function compose_menu($p)
{
$menu = new html_table(array('cols' => 2));
$chbox = new html_checkbox(array('value' => 1));
@ -512,12 +520,10 @@ class enigma_ui
$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());
$menu = html::div(array('id' => 'enigmamenu', 'class' => 'popupmenu'), $menu->show());
$p['content'] .= $menu;
return $p;
// Options menu contents
$this->rc->output->add_footer($menu);
}
/**
@ -646,7 +652,7 @@ class enigma_ui
{
$engine = $this->enigma->load_engine();
// handle attachments vcard attachments
// handle keys/certs in attachments
foreach ((array) $p['object']->attachments as $attachment) {
if ($engine->is_keys_part($attachment)) {
$this->keys_parts[] = $attachment->mime_id;
@ -746,4 +752,45 @@ class enigma_ui
return $p;
}
/**
* Handler for message_compose_body hook
* Display error when the message cannot be encrypted
* and provide a way to try again with a password.
*/
function message_compose($p)
{
$engine = $this->enigma->load_engine();
// skip: message has no signed/encoded content
if (!$this->enigma->engine) {
return $p;
}
$engine = $this->enigma->engine;
// Decryption status
foreach ($engine->decryptions as $status) {
if ($status instanceof enigma_error) {
$code = $status->getCode();
if ($code == enigma_error::E_KEYNOTFOUND) {
$msg = rcube::Q(str_replace('$keyid', enigma_key::format_id($status->getData('id')),
$this->enigma->gettext('decryptnokey')));
}
else if ($code == enigma_error::E_BADPASS) {
$this->password_prompt($status, array('compose-init' => true));
return $p;
}
else {
$msg = rcube::Q($this->enigma->gettext('decrypterror'));
}
}
}
if ($msg) {
$this->rc->output->show_message($msg, 'error');
}
return $p;
}
}

@ -1,5 +1,6 @@
<?php
/*
/**
+-------------------------------------------------------------------------+
| User ID class for the Enigma Plugin |
| |

Loading…
Cancel
Save