From 0499f5094345ca0dbbfba112300ac9579057e4f5 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Wed, 19 Jul 2017 18:16:22 +0200 Subject: [PATCH] W.I.P Smime: - Adapt mail ui to resolve smime messages - Set signature and encryption indicators --- mail/inc/class.mail_ui.inc.php | 308 ++++------------------------- mail/js/app.js | 42 +++- mail/templates/default/display.xet | 5 +- mail/templates/default/index.xet | 10 +- 4 files changed, 90 insertions(+), 275 deletions(-) diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index e44ad88dd1..1a5840809b 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -2038,7 +2038,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2); { if (Mail\Smime::isSmime($attch['type'])) { - $data['smimeSigUrl'] = $attch['mime_url']; + $data['smime'] = Mail\Smime::isSmimeSignatureOnly($attch['type']) ? 'smimeSignature' : 'smimeEncryption'; } } } @@ -2159,46 +2159,12 @@ $filter['before']= date("d-M-Y", $cutoffdate2); // if we are in HTML so its likely that we should show the embedded images; as a result // we do NOT want to see those, that are embedded in the list of attachments if ($htmlOptions !='always_display') $fetchEmbeddedImages = true; - $attachments = $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox); - - foreach ($attachments as &$attachment) - { - if (Mail\Smime::isSmime($attachment['mimeType'])) - { - $smimeData = $this->resolveSmimeMessage ( - $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox), - array( - 'mimeType' => $attachment['mimeType'], - 'uid' => $uid, - 'partID' => $partID, - 'rowID' => $rowID, - 'mailbox' => $mailbox, - 'certAttachedPartID' => $attachment['partID'], - ) - ); - } + try{ + $attachments = $this->mail_bo->getMessageAttachments($uid, $partID, null, $fetchEmbeddedImages,true,true,$mailbox); } - - if (is_array($smimeData)) + catch(Mail\Smime\PassphraseMissing $e) { - $error_msg[] = $smimeData['msg']; - if ($smimeData['signed']) - { - $linkData = array - ( - 'menuaction' => 'mail.mail_ui.getAttachment', - 'id' => $rowID, - 'part' => $smimeData['partID'], - 'is_winmail' => false, - 'mailbox' => base64_encode($mailbox) - ); - $content['smimeSigUrl'] = Egw::link('/index.php',$linkData); - } - if ($smimeData['password_required']) - { - $response = Api\Json\Response::get(); - $response->call('app.mail.smimeRequestPassphrase'); - } + //continue } //error_log(__METHOD__.__LINE__.array2string($attachments)); @@ -2270,152 +2236,6 @@ $filter['before']= date("d-M-Y", $cutoffdate2); $etpl->exec('mail.mail_ui.displayMessage',$content,$sel_options,$readonlys,$preserv,2); } - /** - * Parse SMIME signed message - * - * @param string $_message signed message - * @param type $_mailbox mailbox - * @return array - */ - function parseSmimeSignedMessage ($_message, $_mailbox) - { - $structure = Horde_Mime_Part::parseMessage($_message, array('forcemime'=>true)); - return array ( - 'attachments' => $this->mail_bo->getMessageAttachments('',null,$structure,true, false,true,$_mailbox), - 'body' => $this->mail_bo->getMessageBody('', '', null, $structure, false, $_mailbox, $calendar_part = null) - ); - } - - /** - * decrypt given smime encrypted message - * - * @param string $_message - * @param string $_passphrase - * @return array|string return - */ - function decryptSmimeBody ($_message, $_passphrase = '') - { - $AB_bo = new addressbook_bo(); - $credents = Mail\Credentials::read($this->mail_bo->profileID, Mail\Credentials::SMIME, $GLOBALS['egw_info']['user']['account_id']); - $certkey = $AB_bo->get_smime_keys($GLOBALS['egw_info']['user']['account_email']); - if (!$this->smime->verifyPassphrase($credents['acc_smime_password'], $_passphrase)) - { - return array ( - 'password_required' => true, - 'msg' => 'Authentication failure!' - ); - } - - $params = array ( - 'type' => 'message', - 'pubkey' => $certkey[$GLOBALS['egw_info']['user']['account_email']], - 'privkey' => $credents['acc_smime_password'], - 'passphrase'=> $_passphrase - ); - return $this->smime->decrypt($_message, $params); - } - - /** - * Resolve certificate and encrypted message from smime attachment - * - * @param string $_message - * @param array $_params - * params = array ( - * mimeType => (string) // message mime type - * uid => (string) // message uid - * partID => (int) // message part id - * rowID => (string) // row id (mail nm) - * mailbox => (string) // the mailbox where message is stored - * passphrase => (string) // smime private key passphrase - * certAttachedPartID => (int) // partID of attached smime certificate - * fetchAttachmentWithPartID => (int) // PartID of requested attachment to be fetched - * ) - * - * @return array - * @throws Exception - */ - function resolveSmimeMessage ($_message, $_params) - { - // default params - $params = array_merge(array( - 'partID' => 0, - 'passphrase' => '', - 'certAttachedPartID' => 0 - ), $_params); - - if (!$_message || empty($_params['uid']) || empty($_params['mailbox'])) - { - throw new Exception(__METHOD__.__LINE__.' (): Seems crucial parameter(s) is missing!'); - } - - $this->smime = new Mail\Smime; - - if (!Mail\Smime::isSmimeSignatureOnly($params['mimeType'])) - { - try { - $this->mail_bo->restoreSessionData(); - $_message = $this->decryptSmimeBody($_message, $params['passphrase'] !='' ? - $params['passphrase'] : $this->mail_bo->sessionData['smime_passphrase']); - } catch (Exception $ex) { - return array('msg', $ex->getMessage()); - } - } - try { - $cert = $this->smime->verify($_message); - } catch (Exception $ex) { - // passphrase is required to decrypt the message - if (isset($_message['password_required'])) - { - return $_message; - } - } - - if ($cert) // signed message, it might be encrypted too - { - $message_parts = $this->smime->extractSignedContents($_message); - $cert_data = array ( - 'verify' => $cert->verify, - 'cert' => $cert->cert, - 'msg' => $cert->msg, - 'certHtml' => $this->smime->certToHTML($cert->cert), - 'partID' => $params['certAttachedPartID'], - 'signed' => true, - ); - } - else // only encrypted message - { - $message_parts = Horde_Mime_Part::parseMessage($_message, array('forcemime' => true)); - } - - if (!Mail\Smime::isSmimeSignatureOnly($params['mimeType'])) - { - $dec_attachments = $this->mail_bo->getMessageAttachments($params['uid'],$params['partID'],$message_parts,true,false,true,$params['mailbox']); - // mark attachments as smime encrypted - array_walk ($dec_attachments,function (&$_attachment, $_index, $_mimeType){ - $_attachment['smime_type'] = $_mimeType; - },$params['mimeType']); - - // fetch requested attachment's content - if (!empty($params['fetchAttachmentWithPartID']) && is_array($dec_attachments)) - { - return array( - 'type' => $message_parts[$params['fetchAttachmentWithPartID']]->getType(), - 'charset' => $message_parts[$params['fetchAttachmentWithPartID']]->getContentTypeParameter('charset'), - 'filename' => $message_parts[$params['fetchAttachmentWithPartID']]->getName(), - 'attachment' => $message_parts[$params['fetchAttachmentWithPartID']]->getContents() - ); - } - } - - $result = array( - 'signed' => false, - 'attachments' => is_array($dec_attachments) ? $this->createAttachmentBlock($dec_attachments,$params['rowID'],$params['uid'],$params['mailbox']) : '', - 'message' => $this->mail_bo->getMessageBody($params['uid'], '', null, $message_parts) - ); - - return is_array($cert_data) ? array_merge($result, $cert_data): $result; - } - /** * This is a helper function to trigger Push method * faster than normal 60 sec cycle. @@ -2802,7 +2622,6 @@ $filter['before']= date("d-M-Y", $cutoffdate2); $uid = $hA['msgUID']; $mailbox = $hA['folder']; $icServerID = $hA['profileID']; - $smime_type = $_GET['smime_type']; $rememberServerID = $this->mail_bo->profileID; if ($icServerID && $icServerID != $this->mail_bo->profileID) { @@ -2813,24 +2632,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2); $is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0; $this->mail_bo->reopen($mailbox); - if ($smime_type) - { - $attachment = $this->resolveSmimeMessage( - $this->mail_bo->getMessageRawBody($uid, null, $mailbox), - array( - 'mimeType' => $smime_type, - 'uid' => $uid, - 'partID' => 0, - 'mailbox' => $mailbox, - 'fetchAttachmentWithPartID' => $part - ) - ); - - } - else - { - $attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false); - } + $attachment = $this->mail_bo->getAttachment($uid,$part,$is_winmail,false); $this->mail_bo->closeConnection(); if ($rememberServerID != $this->mail_bo->profileID) { @@ -2901,22 +2703,6 @@ $filter['before']= date("d-M-Y", $cutoffdate2); } //Import failed, download content anyway } - // Display smime certificate - if (Mail\Smime::isSmime($attachment['type'])) - { - $smime = $this->resolveSmimeMessage( - $this->mail_bo->getMessageRawBody($uid, null, $mailbox), - array( - 'mimeType' => $attachment['type'], - 'uid' => $uid, - 'partID' => 0, - 'mailbox' => $mailbox - ) - ); - Api\Header\Content::safe($smime['certHtml'], '', $attachment['type'], $size=0, false, true); - echo $smime['certHtml']; - exit(); - } } //error_log(__METHOD__.__LINE__.'->'.array2string($attachment)); $filename = ($attachment['name']?$attachment['name']:($attachment['filename']?$attachment['filename']:$mailbox.'_uid'.$uid.'_part'.$part)); @@ -3280,56 +3066,42 @@ $filter['before']= date("d-M-Y", $cutoffdate2); $bufferHtmlOptions = $this->mail_bo->htmlOptions; if (empty($htmlOptions)) $htmlOptions = $this->mail_bo->htmlOptions; // fetching structure now, to supply it to getMessageBody and getMessageAttachment, so it does not get fetched twice - $structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false); + try + { + if ($smimePassphrase) + { + Api\Cache::setSession('mail', 'smime_passphrase', $smimePassphrase); + } + $structure = $this->mail_bo->getStructure($uid, $partID, $mailbox, false); + if (($smime = $structure->getMetadata('X-EGroupware-Smime'))) + { + + $attachments = $this->mail_bo->getMessageAttachments($uid, $partID, $structure,true,false,true, $mailbox); + $push = new Api\Json\Push(); + if (is_array($attachments)) + { + $push->call('app.mail.set_smimeAttachments', $this->createAttachmentBlock($attachments, $_GET['_messageID'], $uid, $mailbox)); + } + $push->call('app.mail.set_smimeFlags', $smime); + } + } + catch(Mail\Smime\PassphraseMissing $e) + { + // do NOT include any default CSS + $smimeHtml = $this->get_email_header(). + '
'.lang("This message is smime encrypted and password protected.").'
'. + '
'. + '
'. + '
'. + ''. + ''. + '
'. + '
'; + return $smimeHtml; + } $calendar_part = null; $bodyParts = $this->mail_bo->getMessageBody($uid, ($htmlOptions?$htmlOptions:''), $partID, $structure, false, $mailbox, $calendar_part); - $mimeType = $structure->getType(); - if ($mimeType == 'multipart/signed') - { - $param = $structure->getAllContentTypeParameters(); - $mimeType = $param['protocol']; - } - if (Mail\Smime::isSmime($mimeType)) - { - $smime = $this->resolveSmimeMessage( - $this->mail_bo->getMessageRawBody($uid, $partID, $mailbox), - array( - 'mimeType' => $mimeType, - 'uid' => $uid, - 'partID' => $partID, - 'rowID' => $_GET['_messageID'], - 'mailbox' => $mailbox, - 'passphrase'=> $smimePassphrase - ) - ); - - if ($smime['message'] || $smime['attachments']) - { - if ($smimePassphrase) - { - $this->mail_bo->sessionData['smime_passphrase'] = $smimePassphrase; - $this->mail_bo->saveSessionData(); - } - $bodyParts = $smime['message']; - $push = new Api\Json\Push(); - if (!empty($smime['attachments'])) $push->call('app.mail.set_smimeAttachments', $smime['attachments']); - } - else if ($smime['password_required']) - { - // do NOT include any default CSS - $smimeHtml = $this->get_email_header(). - '
'.lang("This message is smime encrypted and password protected.").'
'. - '
'. - '
'. - '
'. - ''. - ''. - '
'. - '
'; - return $smimeHtml; - } - } - else + if (!$smime) { Api\Session::cache_control(true); } diff --git a/mail/js/app.js b/mail/js/app.js index 7a24d65cd3..53c8014539 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -982,7 +982,21 @@ app.classes.mail = AppJS.extend( if(widget == null) continue; widget.set_value(dataElem.data[data_widgets[id]] || ""); } - + ['smime_signature', 'smime_encryption'].forEach(id => + { + var widget = this.et2.getWidgetById(id); + switch (id) + { + case 'smime_signature': + widget.set_disabled(!(dataElem.data.smime == 'smimeSignature')); + break; + case 'smime_encryption': + widget.set_disabled(!(dataElem.data.smime == 'smimeEncryption')); + break; + default: + widget.set_disabled(true); + } + }); // Blank first, so we don't show previous email while loading var IframeHandle = this.et2.getWidgetById('messageIFRAME'); IframeHandle.set_src('about:blank'); @@ -1018,7 +1032,7 @@ app.classes.mail = AppJS.extend( jQuery(IframeHandle.getDOMNode()).on('load', function(e){ self.resolveExternalImages (this.contentWindow.document); }); - if (dataElem.data['smimeSigUrl']) this.smimeAttachmentsCheckerInterval(); + if (dataElem.data['smime']) this.smimeAttachmentsCheckerInterval(); } var messages = {}; @@ -5753,6 +5767,7 @@ app.classes.mail = AppJS.extend( { content.data[attachmentArea.id] = _attachments; this.et2.setArrayMgr('contnet', content); + attachmentArea.getDOMNode().classList.remove('loading'); attachmentArea.set_value({content:_attachments}); if (attachmentArea.id == 'previewAttachmentArea') { @@ -5773,6 +5788,8 @@ app.classes.mail = AppJS.extend( smimeAttachmentsCheckerInterval:function () { var self = this; + var attachmentArea = this.et2.getWidgetById('previewAttachmentArea'); + if (attachmentArea) attachmentArea.getDOMNode().classList.add('loading'); var interval = window.setInterval(function(){ self.egw.json('mail.mail_ui.ajax_smimeAttachmentsChecker',null,function(_stop){ if (_stop) @@ -5781,5 +5798,26 @@ app.classes.mail = AppJS.extend( } }).sendRequest(true); },1000); + }, + + /** + * + * @param {object} _data smime resolved certificate data + * @returns {undefined} + */ + set_smimeFlags: function (_data) + { + if (!_data) return; + var attachmentArea = this.et2.getWidgetById('previewAttachmentArea'); + if (attachmentArea) attachmentArea.getDOMNode().classList.remove('loading'); + var smime_signature = this.et2.getWidgetById('smime_signature'); + var smime_encryption = this.et2.getWidgetById('smime_encryption'); + + smime_signature.set_disabled(!_data.signed); + smime_encryption.set_disabled(!_data.encrypted); + if (!_data.verify) + { + smime_signature.set_statustext(_data.msg); + } } }); diff --git a/mail/templates/default/display.xet b/mail/templates/default/display.xet index 6c8af5d3b6..e9421f0009 100644 --- a/mail/templates/default/display.xet +++ b/mail/templates/default/display.xet @@ -17,7 +17,6 @@ - @@ -47,6 +46,10 @@ + + + + diff --git a/mail/templates/default/index.xet b/mail/templates/default/index.xet index 5d80ebe4f5..3f6ab9b264 100644 --- a/mail/templates/default/index.xet +++ b/mail/templates/default/index.xet @@ -22,7 +22,6 @@ -