From 8b88959e69972c1a8880bb107dcd3e0bfd00ccee Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 20 May 2015 21:20:10 +0000 Subject: [PATCH] read client-side missing pgp keys from addressbook and offer user to import them --- addressbook/inc/class.addressbook_bo.inc.php | 121 ++++++++++++++++++- phpgwapi/js/jsapi/app_base.js | 35 +++++- 2 files changed, 151 insertions(+), 5 deletions(-) diff --git a/addressbook/inc/class.addressbook_bo.inc.php b/addressbook/inc/class.addressbook_bo.inc.php index a260e2be2e..f2d845c451 100755 --- a/addressbook/inc/class.addressbook_bo.inc.php +++ b/addressbook/inc/class.addressbook_bo.inc.php @@ -284,7 +284,7 @@ class addressbook_bo extends addressbook_so ); //_debug_array($this->contact_fields); $this->own_account_acl = $GLOBALS['egw_info']['server']['own_account_acl']; - if (!is_array($this->own_account_acl)) $this->own_account_acl = unserialize($this->own_account_acl); + if (!is_array($this->own_account_acl)) $this->own_account_acl = json_php_unserialize($this->own_account_acl, true); // we have only one acl (n_fn) for the whole name, as not all backends store every part in an own field if ($this->own_account_acl && in_array('n_fn',$this->own_account_acl)) { @@ -2368,4 +2368,123 @@ class addressbook_bo extends addressbook_so //error_log(__METHOD__.'('.array2string($owner).') returning '.array2string($ctag)); return $ctag; } + + static public $pgp_key_regexp = '/-----BEGIN PGP PUBLIC KEY BLOCK-----.*-----END PGP PUBLIC KEY BLOCK-----\r?\n/s'; + + /** + * Search addressbook for PGP public keys of given recipients + * + * EMail addresses are lowercased to make search case-insensitive + * + * @param string|int|array $recipients (array of) email addresses or numeric account-ids + * @return array email|account_id => key pairs + */ + public function ajax_get_pgp_keys($recipients) + { + if (!$recipients) return array(); + + if (!is_array($recipients)) $recipients = array($recipients); + + $criteria = $result = array(); + foreach($recipients as &$recipient) + { + if (is_numeric($recipient)) + { + $criteria['account_id'][] = (int)$recipient; + } + else + { + $criteria['contact_email'][] = $recipient = strtolower($recipient); + } + } + foreach($this->search($criteria, array('account_id', 'contact_email', 'contact_pubkey'), '', '', '', false, 'OR', false, + "contact_pubkey LIKE '%-----BEGIN PGP PUBLIC KEY BLOCK-----%'") as $contact) + { + $matches = null; + if (preg_match(self::$pgp_key_regexp, $contact['pubkey'], $matches)) + { + $contact['email'] = strtolower($contact['email']); + if (empty($criteria['account_id']) || in_array($contact['email'], $recipients)) + { + $result[$contact['email']] = $matches[0]; + } + else + { + $result[$contact['account_id']] = $matches[0]; + } + } + } + //error_log(__METHOD__."(".array2string($recipients).") returning ".array2string($result)); + egw_json_response::get()->data($result); + } + + /** + * Set PGP keys for given email or account_id, if user has necessary rights + * + * @param array $keys email|account_id => public key pairs to store + * @param boolean $allow_user_updates =null for admins, set config to allow regular users to store their pgp key + * @return int number of pgp keys stored + */ + public function ajax_set_pgp_keys($keys, $allow_user_updates=null) + { + if (isset($allow_user_updates) && isset($GLOBALS['egw_info']['user']['apps']['admin'])) + { + $update = false; + if ($allow_user_updates && !in_array('contact_pubkey', $this->own_account_acl)) + { + $this->own_account_acl[] = 'contact_pubkey'; + $update = true; + } + elseif (!$allow_user_updates && ($key = array_search('contact_pubkey', $this->own_account_acl)) !== false) + { + unset($this->own_account_acl[$key]); + $update = true; + } + if ($update) + { + config::save_value('own_account_acl', $this->own_account_acl, 'phpgwapi'); + } + } + $criteria = array(); + foreach($keys as $recipient => $key) + { + if (!preg_match(self::$pgp_key_regexp, $key)) continue; + + if (is_numeric($recipient)) + { + $criteria['contact_email'][] = $recipient; + } + else + { + $criteria['account_id'][] = (int)$recipient; + } + } + if (!$criteria) return 0; + + $updated = 0; + foreach($this->search($criteria, false, '', '', '', false, 'OR') as $contact) + { + if ($contact['account_id'] && isset($keys[$contact['account_id']])) + { + $key = $keys[$contact['account_id']]; + } + elseif (isset($keys[$contact['email']])) + { + $key = $keys[$contact['email']]; + } + if (empty($contact['pubkey']) || !preg_match(self::$pgp_key_regexp, $contact['pubkey'])) + { + $contact['pubkey'] .= $key; + } + else + { + $contact['pubkey'] = preg_replace(self::$pgp_key_regexp, $key, $contact['pubkey']); + } + if ($this->check_perms(EGW_ACL_EDIT, $contact) && $this->save($contact)) + { + ++$updated; + } + } + return $updated; + } } diff --git a/phpgwapi/js/jsapi/app_base.js b/phpgwapi/js/jsapi/app_base.js index d6a3dc1f5f..a3e6a62ca5 100644 --- a/phpgwapi/js/jsapi/app_base.js +++ b/phpgwapi/js/jsapi/app_base.js @@ -409,7 +409,7 @@ var AppJS = Class.extend( self._refresh_fav_nm(); } }); - + // Bind favorite de-select var egw_fw = egw_getFramework(); if(egw_fw && egw_fw.applications[this.appname] && egw_fw.applications[this.appname].browser @@ -938,12 +938,12 @@ var AppJS = Class.extend( */ mailvelopeGetCheckRecipients: function(_recipients) { - // replace rfc822 addresses with raw email, as Mailvelop does not like them + // replace rfc822 addresses with raw email, as Mailvelop does not like them and lowercase all email var rfc822_preg = /<([^'" <>]+)>$/; var recipients = _recipients.map(function(_recipient) { var matches = _recipient.match(rfc822_preg); - return matches ? matches[1] : _recipient; + return matches ? matches[1].toLowerCase() : _recipient.toLowerCase(); }); // check if we have keys for all recipients @@ -954,6 +954,7 @@ var AppJS = Class.extend( var reject = _reject; self.mailvelopeOpenKeyring().then(function(_keyring) { + var keyring = _keyring; _keyring.validKeyForAddress(recipients).then(function(_status) { var no_key = []; @@ -963,7 +964,33 @@ var AppJS = Class.extend( } if (no_key.length) { - reject(new Error(self.egw.lang('No key for recipient: '+no_key.join(', ')))); + // server addressbook on server for missing public keys + self.egw.json('addressbook.addressbook_bo.ajax_get_pgp_keys', [no_key]).sendRequest().then(function(_data) + { + var data = _data.response['0'].data; + var promises = []; + for(var email in data) + { + promises.push(keyring.importPublicKey(data[email]).then(function(_result) + { + if (_result == 'IMPORTED') + { + no_key.splice(no_key.indexOf(email),1); + } + })); + } + Promise.all(promises).then(function() + { + if (no_key.length) + { + reject(new Error(self.egw.lang('No key for recipient: '+no_key.join(', ')))); + } + else + { + resolve(recipients); + } + }); + }); } else {