Aleksander Machniak
2015-10-03 13eb9bdc245273918bc2b96b475d7d74f01aae2b
plugins/enigma/lib/enigma_ui.php
@@ -1,5 +1,6 @@
<?php
/*
/**
 +-------------------------------------------------------------------------+
 | User Interface for the Enigma Plugin                                    |
 |                                                                         |
@@ -56,6 +57,18 @@
*/
                case 'import':
                    $this->key_import();
                    break;
                case 'export':
                    $this->key_export();
                    break;
                case 'generate':
                    $this->key_generate();
                    break;
                case 'create':
                    $this->key_create();
                    break;
                case 'search':
@@ -131,9 +144,10 @@
    /**
     * 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');
@@ -142,6 +156,10 @@
        }
        $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);
@@ -246,6 +264,7 @@
            }
        }
        $this->rc->output->set_env('rowcount', $size);
        $this->rc->output->set_env('search_request', $search);
        $this->rc->output->set_env('pagecount', ceil($listsize/$pagesize));
        $this->rc->output->set_env('current_page', $page);
@@ -337,23 +356,28 @@
     */
    function tpl_key_data($attrib)
    {
        $out = '';
        $table = new html_table(array('cols' => 2));
        $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());
@@ -361,33 +385,126 @@
        $out .= html::tag('fieldset', null,
            html::tag('legend', null,
                $this->enigma->gettext('basicinfo')) . $table->show($attrib));
/*
        // Subkeys
        $table = new html_table(array('cols' => 6));
        // Columns: Type, ID, Algorithm, Size, Created, Expires
        $table = new html_table(array('cols' => 5, 'id' => 'enigmasubkeytable', 'class' => 'records-table'));
        $table->add_header('id', $this->enigma->gettext('subkeyid'));
        $table->add_header('algo', $this->enigma->gettext('subkeyalgo'));
        $table->add_header('created', $this->enigma->gettext('subkeycreated'));
        $table->add_header('expires', $this->enigma->gettext('subkeyexpires'));
        $table->add_header('usage', $this->enigma->gettext('subkeyusage'));
        $now         = time();
        $date_format = $this->rc->config->get('date_format', 'Y-m-d');
        $usage_map   = array(
            enigma_key::CAN_ENCRYPT => $this->enigma->gettext('typeencrypt'),
            enigma_key::CAN_SIGN    => $this->enigma->gettext('typesign'),
            enigma_key::CAN_CERTIFY => $this->enigma->gettext('typecert'),
            enigma_key::CAN_AUTH    => $this->enigma->gettext('typeauth'),
        );
        foreach ($this->data->subkeys as $subkey) {
            $algo = $subkey->get_algorithm();
            if ($algo && $subkey->length) {
                $algo .= ' (' . $subkey->length . ')';
            }
            $usage = array();
            foreach ($usage_map as $key => $text) {
                if ($subkey->usage & $key) {
                    $usage[] = $text;
                }
            }
            $table->add('id', $subkey->get_short_id());
            $table->add('algo', $algo);
            $table->add('created', $subkey->created ? $this->rc->format_date($subkey->created, $date_format, false) : '');
            $table->add('expires', $subkey->expires ? $this->rc->format_date($subkey->expires, $date_format, false) : $this->enigma->gettext('expiresnever'));
            $table->add('usage', implode(',', $usage));
            $table->set_row_attribs($subkey->revoked || ($subkey->expires && $subkey->expires < $now) ? 'deleted' : '');
        }
        $out .= html::tag('fieldset', null,
            html::tag('legend', null,
                $this->enigma->gettext('subkeys')) . $table->show($attrib));
            html::tag('legend', null,
                $this->enigma->gettext('subkeys')) . $table->show());
        // Additional user IDs
        $table = new html_table(array('cols' => 2));
        // Columns: User ID, Validity
        $table = new html_table(array('cols' => 2, 'id' => 'enigmausertable', 'class' => 'records-table'));
        $table->add_header('id', $this->enigma->gettext('userid'));
        $table->add_header('valid', $this->enigma->gettext('uservalid'));
        foreach ($this->data->users as $user) {
            $username = $user->name;
            if ($user->comment) {
                $username .= ' (' . $user->comment . ')';
            }
            $username .= ' <' . $user->email . '>';
            $table->add('id', rcube::Q(trim($username)));
            $table->add('valid', $this->enigma->gettext($user->valid ? 'valid' : 'unknown'));
            $table->set_row_attribs($user->revoked || !$user->valid ? 'deleted' : '');
        }
        $out .= html::tag('fieldset', null,
            html::tag('legend', null,
                $this->enigma->gettext('userids')) . $table->show($attrib));
*/
            html::tag('legend', null,
                $this->enigma->gettext('userids')) . $table->show());
        return $out;
    }
    /**
     * Key import page handler
     * Key(s) export handler
     */
    private function key_export()
    {
        $keys   = rcube_utils::get_input_value('_keys', rcube_utils::INPUT_GPC);
        $engine = $this->enigma->load_engine();
        $list   = $keys == '*' ? $engine->list_keys() : explode(',', $keys);
        if (is_array($list)) {
            $filename = 'export.pgp';
            if (count($list) == 1) {
                $filename = (is_object($list[0]) ? $list[0]->id : $list[0]) . '.pgp';
            }
            // send downlaod headers
            header('Content-Type: application/pgp-keys');
            header('Content-Disposition: attachment; filename="' . $filename . '"');
            if ($fp = fopen('php://output', 'w')) {
                foreach ($list as $key) {
                    $engine->export_key(is_object($key) ? $key->id : $key, $fp);
                }
            }
        }
        exit;
    }
    /**
     * Key import (page) handler
     */
    private function key_import()
    {
        // Import process
        if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name'])) {
        if ($data = rcube_utils::get_input_value('_keys', rcube_utils::INPUT_POST)) {
            // Import from generation form (ajax request)
            $this->enigma->load_engine();
            $result = $this->enigma->engine->import_key($data);
            if (is_array($result)) {
                $this->rc->output->command('enigma_key_create_success');
                $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation');
            }
            else {
                $this->rc->output->show_message('enigma.keysimportfailed', 'error');
            }
            $this->rc->output->send();
        }
        else if ($_FILES['_file']['tmp_name'] && is_uploaded_file($_FILES['_file']['tmp_name'])) {
            $this->enigma->load_engine();
            $result = $this->enigma->engine->import_key($_FILES['_file']['tmp_name'], true);
@@ -396,8 +513,9 @@
                if ($result['imported']) {
                    $this->rc->output->command('parent.enigma_list', 1);
                }
                else
                else {
                    $this->rc->output->command('parent.enigma_loadframe');
                }
                $this->rc->output->show_message('enigma.keysimportsuccess', 'confirmation',
                    array('new' => $result['imported'], 'old' => $result['unchanged']));
@@ -453,16 +571,117 @@
    }
    /**
     * Server-side key pair generation handler
     */
    private function key_generate()
    {
        $user = rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST, true);
        $pass = rcube_utils::get_input_value('_password', rcube_utils::INPUT_POST, true);
        $size = (int) rcube_utils::get_input_value('_size', rcube_utils::INPUT_POST);
        if ($size > 4096) {
            $size = 4096;
        }
        $ident = rcube_mime::decode_address_list($user, 1, false);
        if (empty($ident)) {
            $this->rc->output->show_message('enigma.keygenerateerror', 'error');
            $this->rc->output->send();
        }
        $this->enigma->load_engine();
        $result = $this->enigma->engine->generate_key(array(
            'user'     => $ident[1]['name'],
            'email'    => $ident[1]['mailto'],
            'password' => $pass,
            'size'     => $size,
        ));
        if ($result instanceof enigma_key) {
            $this->rc->output->command('enigma_key_create_success');
            $this->rc->output->show_message('enigma.keygeneratesuccess', 'confirmation');
        }
        else {
            $this->rc->output->show_message('enigma.keygenerateerror', 'error');
        }
        $this->rc->output->send();
    }
    /**
     * Key generation page handler
     */
    private function key_create()
    {
        $this->enigma->include_script('openpgp.min.js');
        $this->rc->output->add_handlers(array(
            'keyform' => array($this, 'tpl_key_create_form'),
        ));
        $this->rc->output->set_env('enigma_keygen_server', $this->rc->config->get('enigma_keygen_server'));
        $this->rc->output->set_pagetitle($this->enigma->gettext('keygenerate'));
        $this->rc->output->send('enigma.keycreate');
    }
    /**
     * Template object for key generation form
     */
    function tpl_key_create_form($attrib)
    {
        $attrib += array('id' => 'rcmKeyCreateForm');
        $table  = new html_table(array('cols' => 2));
        // get user's identities
        $identities = $this->rc->user->list_identities(null, true);
        // Identity
        $select = new html_select(array('name' => 'identity', 'id' => 'key-ident'));
        foreach ((array) $identities as $idx => $ident) {
            $name = empty($ident['name']) ? ('<' . $ident['email'] . '>') : $ident['ident'];
            $select->add($name, $idx);
        }
        $table->add('title', html::label('key-name', rcube::Q($this->enigma->gettext('newkeyident'))));
        $table->add(null, $select->show(0));
        // Key size
        $select = new html_select(array('name' => 'size', 'id' => 'key-size'));
        $select->add($this->enigma->gettext('key2048'), '2048');
        $select->add($this->enigma->gettext('key4096'), '4096');
        $table->add('title', html::label('key-size', rcube::Q($this->enigma->gettext('newkeysize'))));
        $table->add(null, $select->show());
        // Password and confirm password
        $table->add('title', html::label('key-pass', rcube::Q($this->enigma->gettext('newkeypass'))));
        $table->add(null, rcube_output::get_edit_field('password', '',
            array('id' => 'key-pass', 'size' => $attrib['size'], 'required' => true), 'password'));
        $table->add('title', html::label('key-pass-confirm', rcube::Q($this->enigma->gettext('newkeypassconfirm'))));
        $table->add(null, rcube_output::get_edit_field('password-confirm', '',
            array('id' => 'key-pass-confirm', 'size' => $attrib['size'], 'required' => true), 'password'));
        $this->rc->output->add_gui_object('keyform', $attrib['id']);
        $this->rc->output->add_label('enigma.keygenerating', 'enigma.formerror',
            'enigma.passwordsdiffer', 'enigma.keygenerateerror', 'enigma.nonameident',
            'enigma.keygennosupport');
        return $this->rc->output->form_tag(array(), $table->show($attrib));
    }
    /**
     * Key deleting
     */
    private function key_delete()
    {
        $keys = rcube_utils::get_input_value('_keys', rcube_utils::INPUT_POST);
        $this->enigma->load_engine();
        $keys   = rcube_utils::get_input_value('_keys', rcube_utils::INPUT_POST);
        $engine = $this->enigma->load_engine();
        foreach ((array)$keys as $key) {
            $res = $this->enigma->engine->delete_key($key);
            $res = $engine->delete_key($key);
            if ($res !== true) {
                $this->rc->output->show_message('enigma.keyremoveerror', 'error');
@@ -476,6 +695,9 @@
        $this->rc->output->send();
    }
    /**
     * Init compose UI (add task button and the menu)
     */
    private function compose_ui()
    {
        $this->add_css();
@@ -493,12 +715,6 @@
            '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 +728,10 @@
        $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);
    }
    /**
@@ -559,11 +773,11 @@
                $attrib['class'] = 'enigmaerror';
                $code            = $status->getCode();
                if ($code == enigma_error::E_KEYNOTFOUND) {
                if ($code == enigma_error::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) {
                else if ($code == enigma_error::BADPASS) {
                    $msg = rcube::Q($this->enigma->gettext('decryptbadpass'));
                    $this->password_prompt($status);
                }
@@ -591,7 +805,7 @@
            if ($sig instanceof enigma_signature) {
                $sender = ($sig->name ? $sig->name . ' ' : '') . '<' . $sig->email . '>';
                if ($sig->valid === enigma_error::E_UNVERIFIED) {
                if ($sig->valid === enigma_error::UNVERIFIED) {
                    $attrib['class'] = 'enigmawarning';
                    $msg = str_replace('$sender', $sender, $this->enigma->gettext('sigunverified'));
                    $msg = str_replace('$keyid', $sig->id, $msg);
@@ -606,7 +820,7 @@
                    $msg = rcube::Q(str_replace('$sender', $sender, $this->enigma->gettext('siginvalid')));
                }
            }
            else if ($sig && $sig->getCode() == enigma_error::E_KEYNOTFOUND) {
            else if ($sig && $sig->getCode() == enigma_error::KEYNOTFOUND) {
                $attrib['class'] = 'enigmawarning';
                $msg = rcube::Q(str_replace('$keyid', enigma_key::format_id($sig->getData('id')),
                    $this->enigma->gettext('signokey')));
@@ -646,7 +860,7 @@
    {
        $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;
@@ -725,11 +939,11 @@
        if ($mode && ($status instanceof enigma_error)) {
            $code = $status->getCode();
            if ($code == enigma_error::E_KEYNOTFOUND) {
            if ($code == enigma_error::KEYNOTFOUND) {
                $vars = array('email' => $status->getData('missing'));
                $msg  = 'enigma.' . $mode . 'nokey';
            }
            else if ($code == enigma_error::E_BADPASS) {
            else if ($code == enigma_error::BADPASS) {
                $msg  = 'enigma.' . $mode . 'badpass';
                $type = 'warning';
@@ -746,4 +960,45 @@
        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::KEYNOTFOUND) {
                    $msg = rcube::Q(str_replace('$keyid', enigma_key::format_id($status->getData('id')),
                        $this->enigma->gettext('decryptnokey')));
                }
                else if ($code == enigma_error::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;
    }
}