From 5e7459b011fd5923af6303736bff4b8003b122f1 Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Tue, 18 Nov 2008 01:27:44 -0700 Subject: [PATCH] PGP: implement Viewing keys and signed parts. --- imp/lib/Mime/Viewer/pgp.php | 398 +++++++++++++++----------------------------- 1 file changed, 136 insertions(+), 262 deletions(-) diff --git a/imp/lib/Mime/Viewer/pgp.php b/imp/lib/Mime/Viewer/pgp.php index bcaf925a8..cddeefe73 100644 --- a/imp/lib/Mime/Viewer/pgp.php +++ b/imp/lib/Mime/Viewer/pgp.php @@ -1,19 +1,16 @@ false, + 'embedded' => true, 'full' => false, 'info' => false, - 'inline' => false, + 'inline' => true ); /** - * IMP_PGP object. + * IMP_Crypt_PGP object. * - * @var IMP_PGP + * @var IMP_Crypt_PGP */ - protected $_imp_pgp; + protected $_imppgp; /** * The address of the sender. * * @var string */ - protected $_address; - - /** - * Pointer to the MIME_Contents item. - * - * @var MIME_Contents - */ - protected $_contents = null; + protected $_address = null; /** * Classwide cache for icons for status messages. @@ -73,225 +63,171 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver protected $_status = array(); /** - * The MIME content-type of this part. - * - * @var string - */ - protected $_type = 'text/html'; - - /** - * Render out the currently set contents. - * - * @param array $params An array with a reference to a MIME_Contents - * object. + * Return the rendered inline version of the Horde_Mime_Part object. * - * @return string The rendered text in HTML. + * @return array See Horde_Mime_Viewer_Driver::render(). */ - public function render($params) + protected function _renderInline() { - global $conf, $prefs; - - /* Set the MIME_Contents class variable. */ - $this->_contents = &$params[0]; - - $msg = ''; + if (empty($this->_imppgp) && + !empty($GLOBALS['conf']['utils']['gnupg'])) { + $this->_imppgp = &Horde_Crypt::singleton(array('imp', 'pgp')); + } - if (empty($this->_imp_pgp) && !empty($conf['utils']['gnupg'])) { - $this->_imp_pgp = new IMP_PGP(); + if (Util::getFormData('rawpgpkey')) { + return array( + $this->_mimepart->getMimeId() => array( + 'data' => $this->_mimepart->getContents(), + 'status' => array(), + 'type' => 'text/plain; charset=' . $this->_mimepart->getCharset() + ) + ); } /* Determine the address of the sender. */ - if (empty($this->_address)) { - $base_ob = &$this->_contents->getBaseObjectPtr(); - $this->_address = $base_ob->getFromAddress(); + if (is_null($this->_address)) { + $headers = $this->_params['contents']->getHeaderOb(); + $this->_address = Horde_Mime_Address::bareAddress($headers->getValue('from')); } - /* We need to insert JavaScript code now if PGP support is active. */ - if (!empty($conf['utils']['gnupg']) && - $prefs->getValue('use_pgp') && - !Util::getFormData('rawpgpkey')) { - $msg = Util::bufferOutput(array('Horde', 'addScriptFile'), 'prototype.js', 'horde', true); - $msg .= Util::bufferOutput(array('Horde', 'addScriptFile'), 'popup.js', 'imp', true); + /* We need to insert JS code if PGP support is active. */ + if (!empty($this->_imppgp) && $GLOBALS['prefs']->getValue('use_pgp')) { + Horde::addScriptFile('prototype.js', 'horde', true); + Horde::addScriptFile('popup.js', 'imp', true); } - /* For RFC 2015/3156, there are 3 types of messages: - + multipart/encrypted - + multipart/signed - + application/pgp-keys */ - switch ($this->mime_part->getType()) { + switch ($this->_mimepart->getType()) { case 'application/pgp-keys': - $msg .= $this->_outputPGPKey(); - break; + return $this->_outputPGPKey(); case 'multipart/signed': - case 'application/pgp-signature': - $msg .= $this->_outputPGPSigned(); - break; + return $this->_outputPGPSigned(); - case 'multipart/encrypted': case 'application/pgp-encrypted': - $msg .= $this->_outputPGPEncrypted(); - break; + case 'application/pgp-signature': + case 'multipart/encrypted': + default: + return array(); } - - return $msg; } /** - * Return the content-type of the output. + * If this MIME part can contain embedded MIME parts, and those embedded + * MIME parts exist, return an altered version of the Horde_Mime_Part that + * contains the embedded MIME part information. * - * @return string The MIME content type of the output. + * @return mixed A Horde_Mime_Part with the embedded MIME part information + * or null if no embedded MIME parts exist. */ - public function getType() + protected function _getEmbeddedMimeParts() { - return $this->_type; + if ($this->_mimepart->getType() != 'application/pgp-encrypted') { + return null; + } + + return null; } /** - * Generates HTML output for 'application/pgp-keys' MIME_Parts. + * Generates output for 'application/pgp-keys' MIME_Parts. * * @return string The HTML output. */ protected function _outputPGPKey() { - global $conf, $prefs; - - $mime = &$this->mime_part; - $part = $this->_contents->getDecodedMIMEPart($mime->getMIMEId()); - - if (empty($conf['utils']['gnupg'])) { - $text = '
' . $part->getContents() . '
'; - } elseif (Util::getFormData('rawpgpkey')) { - $text = $part->getContents(); - $this->_type = 'text/plain'; - } else { - require_once 'Horde/Text.php'; - - $pgp_key = $mime->getContents(); - - /* Initialize status message. */ - $this->_initStatus($this->getIcon($mime->getType()), _("PGP")); - $msg = _("This PGP Public Key was attached to the message."); - if ($prefs->getValue('use_pgp') && - $GLOBALS['registry']->hasMethod('contacts/addField') && - $prefs->getValue('add_source')) { - $msg .= ' ' . Horde::link('#', '', '', '', $this->_imp_pgp->savePublicKeyURL($mime) . ' return false;') . _("[Save the key to your Address book]") . ''; - } - $this->_status[] = $msg . ' ' . $this->_contents->linkViewJS($part, 'view_attach', _("[View the raw key]"), '', null, array('rawpgpkey' => 1)); - - $text = $this->_outputStatus(false) . - '' . nl2br(str_replace(' ', ' ', $this->_imp_pgp->pgpPrettyKey($pgp_key))) . ''; + /* Initialize status message. */ + $status = array( + 'icon' => Horde::img('mime/encryption.png', _("PGP")), + 'text' => array( + _("A PGP Public Key was attached to the message.") + ) + ); + + if ($GLOBALS['prefs']->getValue('use_pgp') && + $GLOBALS['prefs']->getValue('add_source') && + $GLOBALS['registry']->hasMethod('contacts/addField')) { + $status['text'][] = Horde::link('#', '', '', '', $this->_imppgp->savePublicKeyURL($mime) . 'return false;') . _("[Save the key to your Address book]") . ''; } - - return $text; + $status['text'][] = $this->_params['contents']->linkViewJS($this->_mimepart, 'view_attach', _("View the raw text of the Public Key."), array('params' => array('mode' => IMP_Contents::RENDER_INLINE, 'rawpgpkey' => 1))); + + return array( + $this->_mimepart->getMimeId() => array( + 'data' => '' . nl2br(str_replace(' ', ' ', $this->_imppgp->pgpPrettyKey($this->_mimepart->getContents()))) . '', + 'status' => array($status), + 'type' => 'text/html; charset=' . NLS::getCharset() + ) + ); } /** - * Generates HTML output for 'multipart/signed' and - * 'application/pgp-signature' MIME_Parts. + * Generates HTML output for 'application/pgp-signature' MIME parts. * * @return string The HTML output. */ protected function _outputPGPSigned() { - global $conf, $prefs; - - $active = ($prefs->getValue('use_pgp') && !empty($conf['utils']['gnupg'])); - $mime = &$this->mime_part; - $mimetype = $mime->getType(); - $text = ''; - - $signenc = $mime->getInformation('pgp_signenc'); - if (!$active) { - if ($signenc) { - $this->_status[] = _("The message below has been digitally signed and encrypted with PGP, but the signature cannot be verified."); - } else { - $this->_status[] = _("The message below has been digitally signed with PGP, but the signature cannot be verified."); - } - } else { - if ($signenc) { - $this->_status[] = _("The message below has been digitally signed and encrypted with PGP."); - } else { - $this->_status[] = _("The message below has been digitally signed with PGP."); - } + $partlist = array_keys($this->_mimepart->contentTypeMap()); + $base_id = reset($partlist); + $signed_id = next($partlist); + $sig_id = Horde_Mime::mimeIdArithmetic($signed_id, 'next'); + + $ret = array( + $base_id => array( + 'data' => '', + 'status' => array( + array( + 'icon' => Horde::img('mime/encryption.png', _("PGP")), + 'text' => array() + ), + ), + 'type' => 'text/html; charset=' . NLS::getCharset() + ), + $sig_id => null + ); + $status = &$ret[$base_id]['status'][0]['text']; + + if (!$GLOBALS['prefs']->getValue('use_pgp') || + empty($GLOBALS['conf']['utils']['gnupg'])) { + /* If PGP not active, hide signature data and output status + * information. */ + $status[] = _("The message below has been digitally signed with PGP, but the signature cannot be verified."); + return $ret; } - $this->_initStatus($this->getIcon($mimetype), _("PGP")); - - /* Store PGP results in $sig_result; store text in $data. */ - $sig_result = null; - if ($mimetype == 'multipart/signed') { - /* If the MIME ID is 0, we need to store the body of the message - in the MIME_Part object. */ - if (!$signenc) { - if (($mimeID = $mime->getMIMEId())) { - $mime->setContents($this->_contents->getBodyPart($mimeID)); - } else { - $mime->setContents($this->_contents->getBody()); - } - $mime->splitContents(); - } + $status[] = _("The message below has been digitally signed with PGP."); - /* Data that is signed appears in the first MIME subpart. */ - $subpart = $mime->getPart($mime->getRelativeMIMEId(1)); - $signature_data = rtrim($subpart->getCanonicalContents(), "\r"); + if ($GLOBALS['prefs']->getValue('pgp_verify') || + Util::getFormData('pgp_verify_msg')) { + $signed_data = Horde_Imap_Client::removeBareNewlines($this->_params['contents']->getBodyPart($signed_id, array('mimeheaders' => true))); + $sig_part = $this->_params['contents']->getMIMEPart($sig_id); + $sig_result = $this->_imppgp->verifySignature($signed_data, $this->_address, $sig_part->getContents()); - $mime_message = Horde_Mime_Message::parseMessage($signature_data); + $graphicsdir = $GLOBALS['registry']->getImageDir('horde'); - /* The PGP signature appears in the second MIME subpart. */ - $subpart = $mime->getPart($mime->getRelativeMIMEId(2)); - if ($subpart && $subpart->getType() == 'application/pgp-signature') { - if ($active) { - if ($GLOBALS['prefs']->getValue('pgp_verify') || - Util::getFormData('pgp_verify_msg')) { - $subpart->transferDecodeContents(); - $sig_result = $this->_imp_pgp->verifySignature($signature_data, $this->_address, $subpart->getContents()); - } elseif (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - $base_ob = &$this->_contents->getBaseObjectPtr(); - $this->_status[] = Horde::link(Util::addParameter(IMP::generateIMPUrl(Horde::selfUrl(), $GLOBALS['imp_mbox']['mailbox'], $GLOBALS['imp_mbox']['index'], $GLOBALS['imp_mbox']['thismailbox']), 'pgp_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; - } - } - } else { - $this->_status[] = _("The message below does not appear to be in the correct PGP format (according to RFC 2015)."); - } - } elseif ($mimetype == 'application/pgp-signature') { - /* Get the signed message to output. */ - $mime_message = new Horde_Mime_Message(); - $mime_message->setType('text/plain'); - $mime->transferDecodeContents(); - - if (empty($this->_imp_pgp)) { - $mime_message->setContents($mime->getContents()); + if (is_a($sig_result, 'PEAR_Error')) { + $icon = Horde::img('alerts/error.png', _("Error"), null, $graphicsdir); + $sig_result = $sig_result->getMessage(); } else { - $mime_message->setContents($this->_imp_pgp->getSignedMessage($mime)); - } - - /* Pass the signed text straight through to PGP program */ - if ($active) { - if ($GLOBALS['prefs']->getValue('pgp_verify') || - Util::getFormData('pgp_verify_msg')) { - $sig_result = $this->_imp_pgp->verifySignature($mime->getContents(), $this->_address); - } elseif (isset($_SESSION['imp']['viewmode']) && - ($_SESSION['imp']['viewmode'] == 'imp')) { - // TODO: Fix to work with DIMP - $this->_status[] = Horde::link(Util::addParameter(Horde::selfUrl(true), 'pgp_verify_msg', 1)) . _("Click HERE to verify the message.") . ''; + $icon = Horde::img('alerts/success.png', _("Success"), null, $graphicsdir); + if (empty($sig_result)) { + $sig_result = _("The message below has been verified."); } } - } - $text = $this->_outputStatus(); - - if ($sig_result !== null) { - $text .= $this->_outputPGPSignatureTest($sig_result); + require_once 'Horde/Text/Filter.php'; + $ret[$base_id]['status'][] = array( + 'icon' => $icon, + 'text' => array( + Text_Filter::filter($sig_result, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)) + ) + ); + } elseif (isset($_SESSION['imp']['viewmode']) && + ($_SESSION['imp']['viewmode'] == 'imp')) { + // TODO: Fix to work with DIMP + $status[] = Horde::link(Util::addParameter(Horde::selfUrl(true), array('pgp_verify_msg' => 1))) . _("Click HERE to verify the message.") . ''; } - /* We need to stick the output into a MIME_Contents object. */ - $mc = new IMP_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$this->_contents)); - $mc->buildMessage(); - - return $text . '' . $mc->getMessage(true) . '
'; + return $ret; } /** @@ -309,11 +245,11 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver $text = ''; $this->_initStatus($this->getIcon($mimetype), _("PGP")); + //$this->_icon = Horde::img($src, $alt); /* Print out message now if PGP is not active. */ if (empty($conf['utils']['gnupg']) || !$prefs->getValue('use_pgp')) { $this->_status[] = _("The message below has been encrypted with PGP, however, PGP support is disabled so the message cannot be decrypted."); - return $this->_outputStatus(); } if ($mimetype == 'multipart/encrypted') { @@ -346,14 +282,12 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver * symmetrically. */ $url = $this->_imp_pgp->getJSOpenWinCode('open_symmetric_passphrase_dialog'); $this->_status[] = Horde::link('#', _("The message below has been encrypted with PGP. You must enter the passphrase that was used to encrypt this message."), null, null, $url . ' return false;') . _("You must enter the passphrase that was used to encrypt this message.") . ''; - $text .= $this->_outputStatus() . - Util::bufferOutput(array('IMP', 'addInlineScript'), $url); + $text .= Util::bufferOutput(array('IMP', 'addInlineScript'), $url); } } elseif (!$literal && !$symmetric && !$this->_imp_pgp->getPersonalPrivateKey()) { /* Output if there is no personal private key to decrypt with. */ $this->_status[] = _("The message below has been encrypted with PGP, however, no personal private key exists so the message cannot be decrypted."); - return $this->_outputStatus(); } elseif (!$literal && !$symmetric && !$this->_imp_pgp->getPassphrase()) { if (isset($_SESSION['imp']['viewmode']) && @@ -363,8 +297,7 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver * asymmetrically. */ $url = $this->_imp_pgp->getJSOpenWinCode('open_passphrase_dialog'); $this->_status[] = Horde::link('#', _("The message below has been encrypted with PGP. You must enter the passphrase for your PGP private key before it can be decrypted."), null, null, $url . ' return false;') . _("You must enter the passphrase for your PGP private key to view this message.") . ''; - $text .= $this->_outputStatus() . - Util::bufferOutput(array('IMP', 'addInlineScript'), $url); + $text .= Util::bufferOutput(array('IMP', 'addInlineScript'), $url); } } else { /* Decrypt this message. */ @@ -374,13 +307,15 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver $decrypted_data = $this->_imp_pgp->decryptMessage($encrypted_data, $symmetric, !$literal); if (is_a($decrypted_data, 'PEAR_Error')) { $this->_status[] = _("The message below does not appear to be a valid PGP encrypted message. Error: ") . $decrypted_data->getMessage(); - $text .= $this->_outputStatus(); } else { /* We need to check if this is a signed/encrypted message. */ $mime_message = Horde_Mime_Message::parseMessage($decrypted_data->message); if (!$mime_message) { require_once 'Horde/Text/Filter.php'; + return !empty($result) + ? $this->_outputPGPSignatureTest($result) + : $this->_outputStatus(); $text .= $this->_signedOutput($decrypted_data->sig_result); $decrypted_message = String::convertCharset($decrypted_data->message, $subpart->getCharset()); $text .= '' . Text_Filter::filter($decrypted_message, 'text2html', array('parselevel' => TEXT_HTML_SYNTAX)) . ''; @@ -392,6 +327,9 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver $mime_message->setContents($decrypted_data->message); $mime_message->splitContents(); } else { + return !empty($result) + ? $this->_outputPGPSignatureTest($result) + : $this->_outputStatus(); $text .= $this->_signedOutput($decrypted_data->sig_result); } @@ -408,14 +346,15 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver } } else { $this->_status[] = _("The message below does not appear to be in the correct PGP format (according to RFC 2015)."); - $text .= $this->_outputStatus(); } } elseif ($mimetype == 'application/pgp-encrypted') { $decrypted_data = $this->_imp_pgp->decryptMessage($encrypted_data, $symmetric, !$literal); if (is_a($decrypted_data, 'PEAR_Error')) { $decrypted_message = $decrypted_data->getMessage(); - $text .= $this->_outputStatus(); } else { + return !empty($result) + ? $this->_outputPGPSignatureTest($result) + : $this->_outputStatus(); $text .= $this->_signedOutput($decrypted_data->sig_result); $decrypted_message = String::convertCharset($decrypted_data->message, $mime->getCharset()); } @@ -428,69 +367,4 @@ class IMP_Horde_Mime_Viewer_pgp extends Horde_Mime_Viewer_Driver return $text; } - - /** - * Generates HTML output for the PGP signature test. - * - * @param string $result Result string of the PGP output concerning the - * signature test. - * - * @return string The HTML output. - */ - protected function _outputPGPSignatureTest($result) - { - $text = ''; - - if (is_a($result, 'PEAR_Error')) { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/error.png', _("Error")); - $result = $result->getMessage(); - } else { - $this->_initStatus($GLOBALS['registry']->getImageDir('horde') . '/alerts/success.png', _("Success")); - /* This message has been verified but there was no output from the - PGP program. */ - if (empty($result)) { - $result = _("The message below has been verified."); - } - } - - require_once 'Horde/Text/Filter.php'; - $this->_status[] = Text_Filter::filter($result, 'text2html', array('parselevel' => TEXT_HTML_NOHTML)); - - return $this->_outputStatus(); - } - - /** - * Output signed message status. - * - * @param string $result The signature result. - * - * @return string HTML output. - */ - protected function _signedOutput($result) - { - if (!empty($result)) { - return $this->_outputPGPSignatureTest($result); - } else { - return $this->_outputStatus(); - } - } - - /* Various formatting helper functions */ - protected function _initStatus($src, $alt = '') - { - if ($this->_icon === null) { - $this->_icon = Horde::img($src, $alt, 'height="16" width="16"', ''); - } - } - - protected function _outputStatus($printable = true) - { - $output = ''; - if (!empty($this->_status)) { - $output = $this->formatStatusMsg($this->_status, $this->_icon, $printable); - } - $this->_icon = null; - $this->_status = array(); - return $output; - } } -- 2.11.0