<?php

require_once IMP_BASE . '/lib/SMIME.php';

/**
 * The IMP_MIME_Viewer_pkcs7 class allows viewing/decrypting of S/MIME
 * messages.
 * This class implements parts of RFC 2630, RFC 2632, and RFC 2633.
 *
 * This class handles the following MIME types:
 *   application/pkcs7-signature
 *   application/pkcs7-mime
 *
 * $Horde: imp/lib/MIME/Viewer/pkcs7.php,v 1.34 2003/06/17 18:55:17 slusarz Exp $
 *
 * Copyright 2002-2003 Mike Cochrane <mike@graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 *
 * @author  Mike Cochrane <mike@graftonhall.co.nz>
 * @version $Revision: 1.34 $
 * @since   IMP 4.0
 * @package imp.mime.viewer
 */
class IMP_MIME_Viewer_pkcs7 extends MIME_Viewer {

    /**
     * IMP_SMIME object.
     *
     * @var object IMP_SMIME $_impSmime
     */
    var $_impSmime;

    /**
     * Is S/MIME support available?
     *
     * @var boolean $_avail
     */
    var $_avail = true;

    /**
     * The headers of the message.
     *
     * @var string $_headers
     */
    var $_headers;

    /**
     * Render out the currently set contents.
     *
     * @access public
     *
     * @param array $params  An array with a reference to a MIME_Contents
     *                       object.
     *
     * @return string  The rendered text in HTML.
     */
    function render($params)
    {
        $contents = &$params[0];

        global $prefs;

        $msg = '';

        if (empty($this->_impSmime)) {
            $this->_impSmime = &new IMP_SMIME();
        }

        /* Get the headers of the message. */
        $this->_getHeaders($contents);

        /* Check to see if S/MIME support is available. */
        if (!$prefs->getValue('use_smime') || 
            is_a($this->_impSmime->checkForOpenSSL(), 'PEAR_Error')) {
            $this->_avail = false;
        } else {
            /* We need to insert JavaScript code now if S/MIME support is
               active. */
            $msg = Horde::bufferOutput('require_once', IMP_TEMPLATES . '/smime/open_smime_win.js');
        }

        switch ($this->mime_part->getType()) {
            case 'multipart/signed':
            case 'application/x-pkcs7-signature':
                $msg .= $this->_outputSMIMESigned($contents);
                break;

            case 'application/x-pkcs7-mime':
                $msg .= $this->_outputSMIMEEncrypted($contents);
                break;
        }

        return $msg;
    }

    function _outputSMIMESigned($contents) 
    {
        global $browser, $prefs, $registry;

        $cert = $text = '';
        $mime = &$this->mime_part;
        $signenc = $mime->getInformation('smime_signenc');
        $sig_result = null;

        if (!$signenc) { 
            $text .= $this->startTableCode($this->getIcon($mime->getType()), _("S/MIME"));
        } 
        $text .= $this->tableElement('<i>' . _("This message has been digitally signed via S/MIME.") . '</i>');

        if (!$this->_avail) {
            $text .= $this->tableElement('<i>' . _("S/MIME support is not enabled so the digital signature is unable to be verified.") . '</i>');
        }

        /* Store S/MIME results in $sig_result. */
        if ($mime->getType() == 'multipart/signed') {
            if (!$signenc) {
                if (($mimeID = $mime->getMIMEId())) {
                    $mime->setContents($contents->getBodyPart($mimeID));
                } else {
                    $mime->setContents($contents->getBody());
                }
                $mime->splitContents();
            }

            /* Data that is signed appears in the first MIME subpart. */
            $signed_part = $mime->getPart($mime->getRelativeMIMEId(1));
            $signed_data = $signed_part->getContents();

            require_once HORDE_BASE . '/lib/MIME/Structure.php';
            $mime_message = &MIME_Structure::parseTextMIMEMessage($signed_data);

            /* The S/MIME signature appears in the second MIME subpart. */
            if ($this->_avail) {
                $subpart = $mime->getPart($mime->getRelativeMIMEId(2));
                if ($subpart->getType() == 'application/x-pkcs7-signature') {
                    if (!$signenc) {
                        $headers = new IMP_Headers();
                        $headers->addHeader('Content-Type', $this->_headers->getValue('Content-Type'));
                        $headers->addHeader('From', $this->_headers->getValue('From'));
                        $sig_result = $this->_impSmime->verifySignature($headers->toString() . $mime->toString());
                        $cert = $this->_impSmime->getSignerCert($headers->toString() . $mime->toString());
                    } else {    
                        $sig_result = $this->_impSmime->verifySignature($signed_data);
                        $cert = $this->_impSmime->getSignerCert($signed_data);
                    }
                }
            }
        } else {
            $text .= $this->tableElement('<i>' . _("This message does not appear to be in the correct S/MIME format (according to RFC 2633).") . '</i>');
        }
        $text .= $this->endTableCode();

        if (!is_null($sig_result)) {
            if (is_a($sig_result, 'PEAR_Error')) {
                $text .= $this->startTableCode($registry->getParam('webroot', 'horde') . '/graphics/alerts/error.gif', _("Error"));
                $sig_result = $sig_result->getMessage();
            } else {
                $text .= $this->startTableCode($registry->getParam('webroot', 'horde') . '/graphics/alerts/success.gif', _("Success"));
                /* This message has been verified. */
                if (empty($sig_result) || ($sig_result === true)) {
                    $sig_result = _("The message has been verified.");
                }
            }

            require_once HORDE_BASE . '/lib/Text.php';
            $text .= $this->tableElement('<i>' . Text::toHTML($sig_result, TEXT_HTML_NOHTML) . '</i>');
            $text .= $this->endTableCode();
        }

        /* We need to stick the output into a MIME_Contents object. */
        $mc = &new MIME_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents));
        $mc->buildMessage();
        $text .= '<table>' . $mc->getMessage(true) . '</table>';

        if (!empty($cert)) {
            $text .= $this->startTableCode($this->getIcon($mime->getType()), _("S/MIME"));
            if ($prefs->getValue('use_smime')) {
                $text .= $this->tableElement(Horde::link('', _("Click to Save S/MIME certificate in your Address book"), null, null, $this->_impSmime->savePublicKeyURL($cert, $this->_headers->getValue('From')) . ' return false;') . '<i>' . _("Click to Save S/MIME certificate in your Address book") . '</i></a>.');
            }
            
            if ($browser->hasFeature('javascript') && $browser->hasFeature('dom')) {
                $text .= $this->tableElement('<i>' . Horde::link('', _("Show S/MIME certificate details."), null, null, "return show_smime_cert();") . _("Show S/MIME certificate details.") . '</a></i>');
                $text .= $this->endTableCode();
                $text .= '<div id="smimeCert" style="display:none;">' . $this->_impSmime->certToHTML($cert) . '</div>';
            } else {
                $text .= $this->tableElement('<i>' . _("S/MIME certificate details:") . '</i>');
                $text .= $this->endTableCode();
                $text .= $this->_outputCertificate($cert);
            }            
        }

        return $text;
    }

    function _outputSMIMEEncrypted($contents) 
    {
        global $prefs, $registry;

        $mime = &$this->mime_part;

        $msg = $this->startTableCode($this->getIcon($mime->getType()), _("S/MIME"));
        $msg .= $this->tableElement('<i>' . _("This message has been encrypted via S/MIME.") . '</i>');

        if (!$this->_avail) {
            $msg .= $this->tableElement('<i>' . _("S/MIME support is not currently enabled so the message is unable to be decrypted.") . '</i>');
            return $msg . $this->endTableCode();
        }

        /* Make sure we have a passphrase. */
        $passphrase = $this->_impSmime->getPassphrase();
        if (empty($passphrase)) {
            $url = $this->_impSmime->getJSOpenWinCode('open_passphrase_dialog');
            $msg .= $this->tableElement(Horde::link('', _("You must enter the passphrase for your S/MIME private key to view this message"), null, null, $url . ' return false;') . '<i>' . _("You must enter the passphrase for your S/MIME private key to view this message") . '</i></a>.');
            $msg .= $this->endTableCode();
            $msg .= '<script language="JavaScript" type="text/javascript">' . $url . ';</script>';
        } else {
            $mime->setContents($contents->getBody());
            $mime->splitContents();

            $headers = new IMP_Headers();
            $headers->addHeader('Content-Type', $this->_headers->getValue('Content-Type'));
            $decrypted_data = $this->_impSmime->decryptMessage($headers->toString() . $mime->toString());

            if (is_a($decrypted_data, 'PEAR_Error')) {
                require_once HORDE_BASE . '/lib/Text.php';
                $msg .= $this->startTableCode($registry->getParam('webroot', 'horde') . '/graphics/alerts/error.gif', _("Error"));
                $msg .= '<i>' . Text::toHTML($decrypted_data->getMessage(), TEXT_HTML_SYNTAX) . '</i>';
                $msg .= $this->endTableCode();
            } else {
                /* We need to check if this is a signed/encrypted
                   message. */
                require_once HORDE_BASE . '/lib/MIME/Structure.php';
                $mime_message = &MIME_Structure::parseTextMIMEMessage($decrypted_data);

                if ($mime_message) {
                    if ($mime_message->getType() == 'multipart/signed') {
                        $mime_message->setInformation('smime_signenc', true);
                        $mime_message->setInformation('smime_headers', $this->_headers);
                    } else {
                        $msg .= $this->endTableCode();
                    }

                    /* We need to stick the output into a MIME_Contents
                       object. */
                    $mc = &new MIME_Contents($mime_message, array('download' => 'download_attach', 'view' => 'view_attach'), array(&$contents));
                    $mc->buildMessage();
                    $msg .= '<table>' . $mc->getMessage(true) . '</table>';
                } else {
                    require_once HORDE_BASE . '/lib/Text.php';
                    $msg .= $this->endTableCode();
                    $msg .= '<span class="fixed">' . Text::toHTML($decrypted_data, TEXT_HTML_SYNTAX) . '</span>';
                }
            }    
        }

        return $msg;
    }

    /**
     * Return text/html as the content-type.
     *
     * @access public
     *
     * @return string  "text/html" constant.
     */
    function getType() 
    {
        return 'text/html';
    }

    /**
     * Get the headers of the S/MIME message.
     *
     * @access private
     *
     * @param object MIME_Contents &$contents  A MIME_Contents/IMP_Contents
     *                                         object.
     */
    function _getHeaders(&$contents)
    {
        if (empty($this->_headers)) {
            if (!($this->_headers = $this->mime_part->getInformation('smime_headers'))) {
                require_once IMP_BASE . '/lib/Headers.php';
                $this->_headers = &new IMP_Headers($contents->getMessageIndex());
                $this->_headers->buildHeaders();
            }
        }
    }

    /* Various formatting helper functions */
    function startTableCode($src, $alt = '')
    {
        $msg = '<table border="0" cellspacing="1" cellpadding="0">';
        $msg .= '<tr><td align="middle">';
        $msg .= '<img src="' . $src . '" height="16" width="16" border="0" alt="' . $alt . '" />&nbsp;';
        $msg .= '</td><td>';
        $msg .= '<table border="0" cellspacing="0" cellpadding="0">';

        return $msg;
    }

    function tableElement($text = '')
    {
        return '<tr><td class="text">' . $text . "</td></tr>\n";
    }

    function endTableCode()
    {
        $msg = "</table></td></tr>\n</table>\n";
        $msg .= "<br />\n";

        return $msg;
    }

}
