diff --git a/CHANGELOG b/CHANGELOG index 4d47e3f9f..cc2b1228e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -7,6 +7,7 @@ CHANGELOG Roundcube Webmail - Display full message subject in onmouseover on truncated subject in mail view (#5346) - Searching in both contacts and groups when LDAP addressbook with group_filters option is used - Update TinyMCE to version 4.3.13 (#5309) +- Enigma: Import keys from key-server(s) (#5286) - Enigma: Search missing public keys on a key-server in mail compose (#5286) - Enigma: Delete user keys when using deluser.sh script - Enigma: Fix redundant list-secret-keys/list-public-keys calls on signing/encryption diff --git a/plugins/enigma/README b/plugins/enigma/README index d29e24340..b02cd7853 100644 --- a/plugins/enigma/README +++ b/plugins/enigma/README @@ -19,6 +19,7 @@ Implemented features: + Handling of PGP keys attached to incoming messages + User preferences to disable plugin features + Attaching public keys to email ++ Key server(s) support (search, import) TODO: @@ -33,7 +34,7 @@ TODO: - export private keys - Generate revocation certs - Search filter to see invalid/expired keys -- Key server(s) support (search, import, upload, refresh) +- Key server(s) support (upload, refresh) - Mark keys as trusted/untrasted, display appropriate message in verify/decrypt status - Change attachment icon on messages list for encrypted messages (like vcard_attachment plugin does) - Support for multi-server installations (store keys in sql database?) diff --git a/plugins/enigma/enigma.js b/plugins/enigma/enigma.js index 2f0d2e2f3..538ece1d5 100644 --- a/plugins/enigma/enigma.js +++ b/plugins/enigma/enigma.js @@ -23,6 +23,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { rcmail.register_command('search', function(props) {return rcmail.enigma_search(props); }, true); rcmail.register_command('reset-search', function(props) {return rcmail.enigma_search_reset(props); }, true); rcmail.register_command('plugin.enigma-import', function() { rcmail.enigma_import(); }, true); + rcmail.register_command('plugin.enigma-import-search', function() { rcmail.enigma_import_search(); }, true); rcmail.register_command('plugin.enigma-key-export', function() { rcmail.enigma_export(); }); rcmail.register_command('plugin.enigma-key-export-selected', function() { rcmail.enigma_export(true); }); rcmail.register_command('plugin.enigma-key-import', function() { rcmail.enigma_key_import(); }, true); @@ -33,6 +34,19 @@ window.rcmail && rcmail.addEventListener('init', function(evt) { rcmail.addEventListener('responseafterplugin.enigmakeys', function() { rcmail.enable_command('plugin.enigma-key-export', rcmail.env.rowcount > 0); }); + + if (rcmail.gui_objects.importform) { + // make sure Enter key in search input starts searching + // instead of submitting the form + $('#rcmimportsearch').keydown(function(e) { + if (e.which == 13) { + rcmail.enigma_import_search(); + return false; + } + }); + + $('input[type="button"]:first').focus(); + } } } else if (rcmail.env.task == 'mail') { @@ -217,6 +231,21 @@ rcube_webmail.prototype.enigma_import = function() } }; +// Ssearch for key(s) for import +rcube_webmail.prototype.enigma_import_search = function() +{ + var form, search; + + if (form = this.gui_objects.importform) { + search = $('#rcmimportsearch').val(); + if (!search) { + return; + } + + this.enigma_find_publickey(search); + } +}; + // list row selection handler rcube_webmail.prototype.enigma_keylist_select = function(list) { @@ -251,6 +280,8 @@ rcube_webmail.prototype.enigma_loadframe = function(url) if (!url && (win = window.frames[this.env.contentframe])) { if (win.location && win.location.href.indexOf(this.env.blankpage) < 0) win.location.href = this.env.blankpage; + if (this.env.frame_lock) + this.set_busy(false, null, this.env.frame_lock); return; } @@ -297,8 +328,11 @@ rcube_webmail.prototype.enigma_search_reset = function(props) }; // Keys/certs listing -rcube_webmail.prototype.enigma_list = function(page) +rcube_webmail.prototype.enigma_list = function(page, reset_frame) { + if (this.is_framed()) + return parent.rcmail.enigma_list(page, reset_frame); + var params = {'_a': 'list'}, lock = this.set_busy(true, 'loading'); @@ -309,7 +343,7 @@ rcube_webmail.prototype.enigma_list = function(page) if (page) params._p = page; - this.enigma_clear_list(); + this.enigma_clear_list(reset_frame); this.http_post('plugin.enigmakeys', params, lock); }; @@ -329,9 +363,11 @@ rcube_webmail.prototype.enigma_list_page = function(page) }; // Remove list rows -rcube_webmail.prototype.enigma_clear_list = function() +rcube_webmail.prototype.enigma_clear_list = function(reset_frame) { - this.enigma_loadframe(); + if (reset_frame !== false) + this.enigma_loadframe(); + if (this.keys_list) this.keys_list.clear(true); @@ -550,6 +586,9 @@ rcube_webmail.prototype.enigma_find_publickey = function(email) var lock = rcmail.set_busy(true, 'enigma.importwait'), post = {_a: 'import', _keys: key}; + if (rcmail.env.action == 'plugin.enigmakeys') + post._refresh = 1; + // send request to server rcmail.http_post('plugin.enigmakeys', post, lock); } diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php index e8a3b5780..c4ca6966e 100644 --- a/plugins/enigma/lib/enigma_ui.php +++ b/plugins/enigma/lib/enigma_ui.php @@ -510,6 +510,10 @@ class enigma_ui else { $this->rc->output->show_message('enigma.keysimportsuccess', 'confirmation', array('new' => $result['imported'], 'old' => $result['unchanged'])); + + if ($result['imported'] && !empty($_POST['_refresh'])) { + $this->rc->output->command('enigma_list', 1, false); + } } } else { @@ -566,20 +570,51 @@ class enigma_ui $upload = new html_inputfield(array('type' => 'file', 'name' => '_file', 'id' => 'rcmimportfile', 'size' => 30)); + $search = new html_inputfield(array('type' => 'text', 'name' => '_search', + 'id' => 'rcmimportsearch', 'size' => 30)); + + $upload_button = new html_inputfield(array( + 'type' => 'button', + 'value' => $this->rc->gettext('import'), + 'class' => 'button', + 'onclick' => "return rcmail.command('plugin.enigma-import','',this,event)", + )); + + $search_button = new html_inputfield(array( + 'type' => 'button', + 'value' => $this->rc->gettext('search'), + 'class' => 'button', + 'onclick' => "return rcmail.command('plugin.enigma-import-search','',this,event)", + )); - $form = html::p(null, + $upload_form = html::div(null, rcube::Q($this->enigma->gettext('keyimporttext'), 'show') . html::br() . html::br() . $upload->show() + . html::br() . html::br() . $upload_button->show() ); - $this->rc->output->add_label('selectimportfile', 'importwait'); + $search_form = html::div(null, + rcube::Q($this->enigma->gettext('keyimportsearchtext'), 'show') + . html::br() . html::br() . $search->show() + . html::br() . html::br() . $search_button->show() + ); + + $form = html::tag('fieldset', '', html::tag('legend', null, $this->enigma->gettext('keyimportlabel')) . $upload_form) + . html::tag('fieldset', '', html::tag('legend', null, $this->enigma->gettext('keyimportsearchlabel')) . $search_form); + + $this->rc->output->add_label('selectimportfile', 'importwait', 'nopubkeyfor', 'nopubkeyforsender', + 'encryptnoattachments','encryptedsendialog','searchpubkeyservers', 'importpubkeys', + 'encryptpubkeysfound', 'search', 'close', 'import', 'keyid', 'keylength', 'keyexpired', + 'keyrevoked', 'keyimportsuccess', 'keyservererror'); $this->rc->output->add_gui_object('importform', $attrib['id']); + $this->rc->output->include_script('publickey.js'); $out = $this->rc->output->form_tag(array( - 'action' => $this->rc->url(array('action' => $this->rc->action, 'a' => 'import')), - 'method' => 'post', + 'action' => $this->rc->url(array('action' => $this->rc->action, 'a' => 'import')), + 'method' => 'post', 'enctype' => 'multipart/form-data') + $attrib, - $form); + $form + ); return $out; } diff --git a/plugins/enigma/localization/en_US.inc b/plugins/enigma/localization/en_US.inc index 8618b996f..3e93f7926 100644 --- a/plugins/enigma/localization/en_US.inc +++ b/plugins/enigma/localization/en_US.inc @@ -89,6 +89,8 @@ $labels['keyexportprompt'] = 'Do you want to include secret keys in the saved Op $labels['onlypubkeys'] = 'Export Public Keys Only'; $labels['withprivkeys'] = 'Export Secret Keys'; $labels['findkey'] = 'Search on key server(s)'; +$labels['keyimportlabel'] = 'Import from file'; +$labels['keyimportsearchlabel'] = 'Search on key server(s)'; $messages = array(); $messages['sigvalid'] = 'Verified signature from $sender.'; @@ -120,6 +122,7 @@ $messages['keyremoveconfirm'] = 'Are you sure, you want to delete selected key(s $messages['keyremovesuccess'] = 'Key(s) deleted successfully'; $messages['keyremoveerror'] = 'Unable to delete selected key(s).'; $messages['keyimporttext'] = 'You can import private and public key(s) or revocation signatures in ASCII-Armor format.'; +$messages['keyimportsearchtext'] = 'You can search for public keys by key identifier, user name or email address and then import them directly.'; $messages['formerror'] = 'Please, fill the form. All fields are required!'; $messages['passwordsdiffer'] = 'Passwords do not match!'; diff --git a/plugins/enigma/skins/classic/enigma.css b/plugins/enigma/skins/classic/enigma.css index 68505065f..8f72fe138 100644 --- a/plugins/enigma/skins/classic/enigma.css +++ b/plugins/enigma/skins/classic/enigma.css @@ -151,6 +151,11 @@ div.enigmascreen border: 0; } +#keyimportform fieldset div { + color: #666; + padding: 5px 0px; +} + #keystoolbar { position: absolute; diff --git a/plugins/enigma/skins/classic/templates/keyimport.html b/plugins/enigma/skins/classic/templates/keyimport.html index 63163ec54..4f0e5ec9e 100644 --- a/plugins/enigma/skins/classic/templates/keyimport.html +++ b/plugins/enigma/skins/classic/templates/keyimport.html @@ -9,10 +9,7 @@
- -

-
-

+
diff --git a/plugins/enigma/skins/larry/enigma.css b/plugins/enigma/skins/larry/enigma.css index f9ea49999..d02fe7d5e 100644 --- a/plugins/enigma/skins/larry/enigma.css +++ b/plugins/enigma/skins/larry/enigma.css @@ -141,6 +141,12 @@ p.enigmaattachment a { text-overflow: ellipsis; } +#keyimportform fieldset div +{ + background-color: #eee; + padding: 10px; +} + #keystoolbar { position: absolute; diff --git a/plugins/enigma/skins/larry/templates/keyimport.html b/plugins/enigma/skins/larry/templates/keyimport.html index cd7979ee2..8a1df35d2 100644 --- a/plugins/enigma/skins/larry/templates/keyimport.html +++ b/plugins/enigma/skins/larry/templates/keyimport.html @@ -9,11 +9,7 @@

- -
-
- -
+
diff --git a/program/js/app.js b/program/js/app.js index cb9bef51e..f6822a9d0 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3808,7 +3808,7 @@ function rcube_webmail() // them into the local Maivelope keyring this.mailvelope_key_import_dialog = function(candidates, import_handler) { - var ul = $('
').addClass('listing mailvelopekeyimport'); + var ul = $('
').addClass('listing pgpkeyimport'); $.each(candidates, function(i, keyrec) { var li = $('
').addClass('key'); if (keyrec.revoked) li.addClass('revoked'); @@ -3862,8 +3862,8 @@ function rcube_webmail() ref.get_label('importpubkeys'), [{ text: ref.get_label('close'), - click: function(){ - $(this).dialog('close'); + click: function() { + (ref.is_framed() ? parent.$ : $)(this).dialog('close'); } }] ); diff --git a/skins/classic/common.css b/skins/classic/common.css index 0c6bffd5a..2773cb100 100644 --- a/skins/classic/common.css +++ b/skins/classic/common.css @@ -1556,3 +1556,57 @@ table.quota-info td.root { -moz-box-shadow: 0 0 5px 0 #999; -o-box-shadow: 0 0 5px 0 #999; } + +/** PGP key import dialog **/ +.pgpkeyimport div.key { + position: relative; + margin-bottom: 2px; + padding: 1em 1em 5px; + background-color: #ebebeb; +} + +.pgpkeyimport div.key.revoked, +.pgpkeyimport div.key.disabled { + color: #a0a0a0; +} + +.pgpkeyimport div.key label { + display: inline-block; + margin-right: 0.5em; +} + +.pgpkeyimport div.key label:after { + content: ":"; +} + +.pgpkeyimport div.key label + a, +.pgpkeyimport div.key label + span { + display: inline-block; + margin-right: 2em; + white-space: nowrap; +} + +.pgpkeyimport div.key label + a { + font-weight: bold; +} + +.pgpkeyimport ul.uids { + margin: 5px 1em 0 1em; + padding: 0; +} + +.pgpkeyimport li.uid { + border: 0; + padding: 2px; +} + +.pgpkeyimport div.key input.button.importkey { + position: absolute; + top: 0.8em; + right: 0.8em; + padding: 2px 6px; +} + +.pgpkeyimport div.key input.button[disabled] { + display: none; +} diff --git a/skins/classic/mail.css b/skins/classic/mail.css index 67af75751..f996090be 100644 --- a/skins/classic/mail.css +++ b/skins/classic/mail.css @@ -1736,56 +1736,3 @@ input.from_address top: 45px; position: absolute; } - -.mailvelopekeyimport div.key { - position: relative; - margin-bottom: 2px; - padding: 1em 1em 5px; - background-color: #ebebeb; -} - -.mailvelopekeyimport div.key.revoked, -.mailvelopekeyimport div.key.disabled { - color: #a0a0a0; -} - -.mailvelopekeyimport div.key label { - display: inline-block; - margin-right: 0.5em; -} - -.mailvelopekeyimport div.key label:after { - content: ":"; -} - -.mailvelopekeyimport div.key label + a, -.mailvelopekeyimport div.key label + span { - display: inline-block; - margin-right: 2em; - white-space: nowrap; -} - -.mailvelopekeyimport div.key label + a { - font-weight: bold; -} - -.mailvelopekeyimport ul.uids { - margin: 5px 1em 0 1em; - padding: 0; -} - -.mailvelopekeyimport li.uid { - border: 0; - padding: 2px; -} - -.mailvelopekeyimport div.key input.button.importkey { - position: absolute; - top: 0.8em; - right: 0.8em; - padding: 2px 6px; -} - -.mailvelopekeyimport div.key input.button[disabled] { - display: none; -} diff --git a/skins/larry/mail.css b/skins/larry/mail.css index 64c7d7afe..68d2c1fc7 100644 --- a/skins/larry/mail.css +++ b/skins/larry/mail.css @@ -1410,56 +1410,3 @@ div.message-partheaders .headers-table td.header { #uploadform form div { margin: 4px 0; } - -.mailvelopekeyimport div.key { - position: relative; - margin-bottom: 2px; - padding: 1em; - background-color: #ebebeb; -} - -.mailvelopekeyimport div.key.revoked, -.mailvelopekeyimport div.key.disabled { - color: #a0a0a0; -} - -.mailvelopekeyimport div.key label { - display: inline-block; - margin-right: 0.5em; -} - -.mailvelopekeyimport div.key label:after { - content: ":"; -} - -.mailvelopekeyimport div.key label + a, -.mailvelopekeyimport div.key label + span { - display: inline-block; - margin-right: 2em; - white-space: nowrap; -} - -.mailvelopekeyimport div.key label + a { - font-weight: bold; -} - -.mailvelopekeyimport ul.uids { - margin: 1em 0 0 0; - padding: 0; -} - -.mailvelopekeyimport li.uid { - border: 0; - padding: 0.3em; -} - -.mailvelopekeyimport div.key input.button.importkey { - position: absolute; - top: 0.8em; - right: 0.8em; - padding: 4px 6px; -} - -.mailvelopekeyimport div.key input.button[disabled] { - display: none; -} diff --git a/skins/larry/styles.css b/skins/larry/styles.css index 132156415..b499c40e2 100644 --- a/skins/larry/styles.css +++ b/skins/larry/styles.css @@ -3160,3 +3160,57 @@ _:not(), _:-moz-handler-blocked, .mozilla .mce-btn-small i { box-shadow: none; outline: none; } + +/** PGP Key import dialog **/ +.pgpkeyimport div.key { + position: relative; + margin-bottom: 2px; + padding: 1em; + background-color: #ebebeb; +} + +.pgpkeyimport div.key.revoked, +.pgpkeyimport div.key.disabled { + color: #a0a0a0; +} + +.pgpkeyimport div.key label { + display: inline-block; + margin-right: 0.5em; +} + +.pgpkeyimport div.key label:after { + content: ":"; +} + +.pgpkeyimport div.key label + a, +.pgpkeyimport div.key label + span { + display: inline-block; + margin-right: 2em; + white-space: nowrap; +} + +.pgpkeyimport div.key label + a { + font-weight: bold; +} + +.pgpkeyimport ul.uids { + margin: 1em 0 0 0; + padding: 0; +} + +.pgpkeyimport li.uid { + border: 0; + padding: 0.3em; +} + +.pgpkeyimport div.key input.button.importkey { + position: absolute; + top: 0.8em; + right: 0.8em; + padding: 4px 6px; +} + +.pgpkeyimport div.key input.button[disabled] { + display: none; +}