WIP of SMIME support:

- Implement smime ecryption in compose
- Implement passphrase dialog
This commit is contained in:
Hadi Nategh 2017-02-15 12:23:27 +01:00
parent e1e9ab8f8e
commit 116151a092
4 changed files with 151 additions and 53 deletions

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay PUBLIC "-//EGroupware GmbH//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
<!-- $Id$ -->
<overlay>
<template id="etemplate.password" template="" lang="" group="0" version="16.1">
<vbox width="250px" class="ui-dialog-content">
<label class="et2_fullWidth" id="message"/>
<hbox>
<image src="password" class="dialog_icon"/>
<hbox>
<passwd id="value" width="80%" blur="enter your passphrase"/>
</hbox>
</hbox>
</vbox>
</template>
</overlay>

View File

@ -212,13 +212,21 @@ class mail_compose
'onExecute' => 'javaScript:app.mail.compose_saveDraft2fm', 'onExecute' => 'javaScript:app.mail.compose_saveDraft2fm',
'hint' => 'Save the drafted message as eml file into VFS' 'hint' => 'Save the drafted message as eml file into VFS'
), ),
'sign' => array ( 'smime_sign' => array (
'caption' => 'Sign', 'caption' => 'Sign',
'icon' => 'smimeSignature', 'icon' => 'smimeSignature',
'group' => ++$group, 'group' => ++$group,
'onExecute' => 'javaScript:app.mail.compose_setToggle', 'onExecute' => 'javaScript:app.mail.compose_setToggle',
'checkbox' => true, 'checkbox' => true,
'hint' => 'Sign your message with smime certificate' 'hint' => 'Sign your message with smime certificate'
),
'smime_encrypt' => array (
'caption' => 'SMIME',
'icon' => 'smimeEncryption',
'group' => ++$group,
'onExecute' => 'javaScript:app.mail.compose_setToggle',
'checkbox' => true,
'hint' => 'Encrypt your message with smime certificate'
) )
); );
foreach (self::$priorities as $key => $priority) foreach (self::$priorities as $key => $priority)
@ -2346,61 +2354,60 @@ class mail_compose
*/ */
if ($_formData['attachments'] && $_formData['filemode'] != Vfs\Sharing::ATTACH && !$_autosaving) if ($_formData['attachments'] && $_formData['filemode'] != Vfs\Sharing::ATTACH && !$_autosaving)
{ {
$attachment_links = $this->getAttachmentLinks($_formData['attachments'], $_formData['filemode'], $attachment_links = $this->_getAttachmentLinks($_formData['attachments'], $_formData['filemode'],
$_formData['mimeType'] == 'html', $_formData['mimeType'] == 'html',
array_unique(array_merge((array)$_formData['to'], (array)$_formData['cc'], (array)$_formData['bcc'])), array_unique(array_merge((array)$_formData['to'], (array)$_formData['cc'], (array)$_formData['bcc'])),
$_formData['expiration'], $_formData['password']); $_formData['expiration'], $_formData['password']);
} }
if($_formData['mimeType'] == 'html') switch ($_formData['mimeType'])
{ {
$body = $_formData['body']; case 'html':
if ($attachment_links) $body = $_formData['body'];
{ if ($attachment_links)
if (strpos($body, '<!-- HTMLSIGBEGIN -->') !== false)
{ {
$body = str_replace('<!-- HTMLSIGBEGIN -->', $attachment_links.'<!-- HTMLSIGBEGIN -->', $body); if (strpos($body, '<!-- HTMLSIGBEGIN -->') !== false)
{
$body = str_replace('<!-- HTMLSIGBEGIN -->', $attachment_links.'<!-- HTMLSIGBEGIN -->', $body);
}
else
{
$body .= $attachment_links;
}
}
if(!empty($signature))
{
$_mailObject->setBody($this->convertHTMLToText($body, true, true).
($disableRuler ? "\r\n" : "\r\n-- \r\n").
$this->convertHTMLToText($signature, true, true));
$body .= ($disableRuler ?'<br>':'<hr style="border:1px dotted silver; width:90%;">').$signature;
} }
else else
{ {
$body .= $attachment_links; $_mailObject->setBody($this->convertHTMLToText($body, true, true));
} }
} // convert URL Images to inline images - if possible
if(!empty($signature)) if (!$_autosaving) $inline_images = Mail::processURL2InlineImages($_mailObject, $body, $mail_bo);
{ if (strpos($body,"<!-- HTMLSIGBEGIN -->")!==false)
$_mailObject->setBody($this->convertHTMLToText($body, true, true). {
($disableRuler ? "\r\n" : "\r\n-- \r\n"). $body = str_replace(array('<!-- HTMLSIGBEGIN -->','<!-- HTMLSIGEND -->'),'',$body);
$this->convertHTMLToText($signature, true, true)); }
$_mailObject->setHtmlBody($body, null, false); // false = no automatic alternative, we called setBody()
break;
case 'openpgp':
$_mailObject->setOpenPgpBody($_formData['body']);
break;
default:
$body = $this->convertHTMLToText($_formData['body'],false);
$body .= ($disableRuler ?'<br>':'<hr style="border:1px dotted silver; width:90%;">').$signature; if ($attachment_links) $body .= $attachment_links;
}
else
{
$_mailObject->setBody($this->convertHTMLToText($body, true, true));
}
// convert URL Images to inline images - if possible
if (!$_autosaving) $inline_images = Mail::processURL2InlineImages($_mailObject, $body, $mail_bo);
if (strpos($body,"<!-- HTMLSIGBEGIN -->")!==false)
{
$body = str_replace(array('<!-- HTMLSIGBEGIN -->','<!-- HTMLSIGEND -->'),'',$body);
}
$_mailObject->setHtmlBody($body, null, false); // false = no automatic alternative, we called setBody()
}
elseif ($_formData['mimeType'] == 'openpgp')
{
$_mailObject->setOpenPgpBody($_formData['body']);
}
else
{
$body = $this->convertHTMLToText($_formData['body'],false);
if ($attachment_links) $body .= $attachment_links; #$_mailObject->Body = $_formData['body'];
if(!empty($signature)) {
#$_mailObject->Body = $_formData['body']; $body .= ($disableRuler ?"\r\n":"\r\n-- \r\n").
if(!empty($signature)) { $this->convertHTMLToText($signature,true,true);
$body .= ($disableRuler ?"\r\n":"\r\n-- \r\n"). }
$this->convertHTMLToText($signature,true,true); $_mailObject->setBody($body);
}
$_mailObject->setBody($body);
} }
//error_log(__METHOD__.__LINE__.array2string($_formData['attachments'])); //error_log(__METHOD__.__LINE__.array2string($_formData['attachments']));
// add the attachments // add the attachments
@ -2499,7 +2506,7 @@ class mail_compose
* @param string $password =null * @param string $password =null
* @return string might be empty if no file attachments found * @return string might be empty if no file attachments found
*/ */
protected function getAttachmentLinks(array $attachments, $filemode, $html, $recipients=array(), $expiration=null, $password=null) protected function _getAttachmentLinks(array $attachments, $filemode, $html, $recipients=array(), $expiration=null, $password=null)
{ {
if ($filemode == Vfs\Sharing::ATTACH) return ''; if ($filemode == Vfs\Sharing::ATTACH) return '';
@ -2783,6 +2790,8 @@ class mail_compose
$this->sessionData['to_infolog'] = $_formData['to_infolog']; $this->sessionData['to_infolog'] = $_formData['to_infolog'];
$this->sessionData['to_tracker'] = $_formData['to_tracker']; $this->sessionData['to_tracker'] = $_formData['to_tracker'];
$this->sessionData['attachments'] = $_formData['attachments']; $this->sessionData['attachments'] = $_formData['attachments'];
$this->sessionData['smime_sign'] = $_formData['smime_sign'];
$this->sessionData['smime_encrypt'] = $_formData['smime_encrypt'];
if (isset($_formData['lastDrafted']) && !empty($_formData['lastDrafted'])) if (isset($_formData['lastDrafted']) && !empty($_formData['lastDrafted']))
{ {
@ -2966,6 +2975,43 @@ class mail_compose
#error_log($this->errorInfo); #error_log($this->errorInfo);
return false; return false;
} }
// SMIME SIGN/ENCRYPTION
if ($_formData['smime_sign'] == 'on' || $_formData['smime_encrypt'] == 'on' )
{
try {
if ($_formData['smime_sign'] == 'on')
{
$smime_part = $this->_encrypt(
'',//TODO
$_formData['smime_encrypt'] == 'on'? Mail\Smime::TYPE_SIGN_ENCRYPT: Mail\Smime::TYPE_SIGN,
$_formData['to'],
$identity['ident_email'],
$_formData['smime_passphrase']
);
if ($smime_part['smime_pass_require'])
{
$response = Api\Json\Response::get();
$response->call('app.mail.smimePassDialog');
return false;
}
}
elseif ($_formData['smime_sign'] == 'off' && $_formData['smime_encrypt'] == 'on')
{
$smime_part = $this->_encrypt(
'',//TODO
Mail\Smime::TYPE_ENCRYPT,
$_formData['to'],
$identity['ident_email']
);
}
//TODO Set signed or encrypted mime part
}
catch (Exception $ex)
{
throw new Api\Exception\WrongUserinput($ex->getMessage());
}
}
// set a higher timeout for big messages // set a higher timeout for big messages
@set_time_limit(120); @set_time_limit(120);
@ -3581,30 +3627,35 @@ class mail_compose
* @return Horde_Mime_Part returns encrypted message * @return Horde_Mime_Part returns encrypted message
* @throws Api\Exception\WrongUserinput if no certificate found * @throws Api\Exception\WrongUserinput if no certificate found
*/ */
function _encrypt(Horde_Mime_part $message, $type, $recipients, $sender) protected function _encrypt(Horde_Mime_part $message, $type, $recipients, $sender, $passphrase='')
{ {
$AB = new addressbook_bo(); $AB = new addressbook_bo();
$smime = new Mail\Smime();
if (isset($sender) && ($type == Mail\Smime::TYPE_SIGN || $type == Mail\Smime::TYPE_SIGN_ENCRYPT)) if (isset($sender) && ($type == Mail\Smime::TYPE_SIGN || $type == Mail\Smime::TYPE_SIGN_ENCRYPT))
{ {
$sender_cert = $AB->get_smime_keys($sender); $sender_cert = $AB->get_smime_keys($sender);
$smime = new Mail\Smime();
if ($sender_cert) if ($sender_cert)
{ {
$senderPubKey = $smime->get_publickey($sender_cert[$sender]); $senderPubKey = $sender_cert[$sender];
} }
else else
{ {
throw new Api\Exception\WrongUserinput('no certificate found to sign the messase'); throw new Api\Exception\WrongUserinput('no certificate found to sign the messase');
} }
$credents = Mail\Credentials::read($this->mail_bo->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']);
$privkey = $credents['acc_smime_password'];
if (!$smime->verifyPassphrase($privkey, $passphrase))
{
return array('smime_pass_require' => true);
}
} }
if (isset($recipients) && ($type == Mail\Smime::TYPE_ENCRYPT || $type == Mail\Smime::TYPE_SIGN_ENCRYPT)) if (isset($recipients) && ($type == Mail\Smime::TYPE_ENCRYPT || $type == Mail\Smime::TYPE_SIGN_ENCRYPT))
{ {
$recipients_certs = $AB->get_smime_keys($recipients); $recipients_certs = $AB->get_smime_keys($recipients);
$recipientsPubKeys = array_Map(array ($smime, 'get_publickey'), $recipients_certs);
if (!$recipients_certs) throw new Api\Exception\WrongUserinput('no certificate found from the recipients to sign/encrypt the messase'); if (!$recipients_certs) throw new Api\Exception\WrongUserinput('no certificate found from the recipients to sign/encrypt the messase');
} }
@ -3612,15 +3663,15 @@ class mail_compose
$sign_params = array( $sign_params = array(
'type' => 'signature', 'type' => 'signature',
'pubkey' => $senderPubKey, 'pubkey' => $senderPubKey,
'prikey' => '', 'privkey' => $privkey,
'passphrase'=> '', 'passphrase'=> $passphrase,
'sigtype' => 'detach', 'sigtype' => 'detach',
'certs' => '' 'certs' => ''
); );
// parameters to pass on for encrypt mime part // parameters to pass on for encrypt mime part
$encrypt_params = array( $encrypt_params = array(
'type' => 'message', 'type' => 'message',
'pubkey' => $recipientsPubKeys 'pubkey' => $recipients_certs
); );
switch ($type) switch ($type)
{ {

View File

@ -5528,5 +5528,33 @@ app.classes.mail = AppJS.extend(
url = this.et2.getArrayMgr("content").getEntry('smimeSigUrl'); url = this.et2.getArrayMgr("content").getEntry('smimeSigUrl');
} }
window.egw.openPopup(url,'700','400'); window.egw.openPopup(url,'700','400');
},
/**
* smime password dialog
*/
smimePassDialog: function ()
{
var self = this;
et2_createWidget("dialog",
{
callback: function(_button_id, _value)
{
if (_button_id && _value)
{
var pass = self.et2.getWidgetById('smime_passphrase');
pass.set_value(_value.value);
}
},
title: egw.lang('Request for passphrase'),
buttons: et2_dialog.BUTTONS_OK_CANCEL,
value:{
content:{
value: '',
message: self.egw.lang('Looks like your certificate is password protected. Please enter your passphrase and try to send again.')
}},
template: egw.webserverUrl+'/api/templates/default/password.xet',
resizable: false
}, et2_dialog._create_parent('mail'));
} }
}); });

View File

@ -12,6 +12,9 @@
<checkbox statustext="check to save as trackerentry on send" id="to_tracker" options="on,off"/> <checkbox statustext="check to save as trackerentry on send" id="to_tracker" options="on,off"/>
<checkbox statustext="check to save as calendar event on send" id="to_calendar" options="on,off"/> <checkbox statustext="check to save as calendar event on send" id="to_calendar" options="on,off"/>
<checkbox statustext="check to recieve a notification when the message is read (note: not all clients support this and/or the reciever may not authorize the notification)" id="disposition" options="on,off"/> <checkbox statustext="check to recieve a notification when the message is read (note: not all clients support this and/or the reciever may not authorize the notification)" id="disposition" options="on,off"/>
<checkbox statustext="check to sign the message on send" id="smime_sign" options="on,off"/>
<checkbox statustext="check to encrypt the message on send" id="smime_encrypt" options="on,off"/>
<passwd id="smime_passphrase"/>
<taglist id="to_integrate_ids"/> <taglist id="to_integrate_ids"/>
<menulist> <menulist>
<menupopup id="priority"/> <menupopup id="priority"/>