<?php
/*
 * $Horde: imp/lib/MIME/Viewer/enriched.php,v 1.2.2.8 2003/01/03 13:23:15 jan Exp $
 *
 * Copyright 2001-2003 Eric Rostetter <eric.rostetter@physics.utexas.edu>
 *
 * See the enclosed file COPYING for license information (GPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
 */

/**
 * The IMP_MIME_Viewer_enriched class renders out plain text from
 * enriched content tags, ala RFC 1896
 *
 * @author  Eric Rostetter <eric.rostetter@physics.utexas.edu>
 * @version $Revision: 1.2.2.8 $
 * @since   IMP 3.1
 * @package horde.mime.viewer
 */

// By RFC, we must do the minimal conformance measures of: A minimal
// text/enriched implementation is one that converts "<<" to "<",
// removes everything between a <param> command and the next balancing
// </param> removes all other formatting commands (all text enclosed
// in angle brackets), and outside of <nofill> environments converts
// any series of n CRLFs to n-1 CRLFs, and converts any lone CRLF
// pairs to SPACE.
//
// We don't qualify as we don't currently track the <nofill>
// environment, that is we do CRLF conversion even if <nofill> is
// specified in the text, but we're close at least.

class IMP_MIME_Viewer_enriched extends MIME_Viewer {

    /**
     * Render out the currently set contents in HTML format.  The
     * $mime_part class variable has the information to render out,
     * encapsulated in a MIME_Part object.
     */
    function render(&$mime)
    {
        global $conf, $prefs;

        include_once HORDE_BASE . '/lib/Text.php';

        $text = $this->mime_part->getContents();
        if ($text === false) {
            return '<i>' . _("There was an error displaying this message part") . '</i>';
        }

        if (trim($text) == '') {
            return $text;
        }

        // We add space at the beginning and end of the string as it will
        // make some regular expression checks later much easier (so we
        // don't have to worry about start/end of line characters)
        $text = ' ' . $text . ' ';

        $charset = $this->getCharset($mime);

        // We need to preserve << tags, so map them to ascii 1 or ascii 255
        // We make the assumption here that there would never be an ascii
        // 1 in an email, which may not be valid, but seems reasonable...
        // ascii 255 would work if for some reason you don't like ascii 1
        // ascii 0 does NOT seem to work for this, though I'm not sure why
        $text = str_replace('<<', chr(1), $text);
        // $text = str_replace('<<', chr(255), $text);

        // Remove any unrecognized tags in the text (via RFC minimal specs)
        // any tags we just don't want to implement can also be removed here
        // Note that this will remove any html links, but this is intended
        $implementedTags = '<param><bold><italic><underline><fixed><excerpt>' .
                           '<smaller><bigger><center><color><fontfamily>' .
                           '<flushleft><flushright><flushboth><paraindent>';
        // $unImplementedTags = '<nofill><lang>';
        $text = strip_tags($text, $implementedTags);

        // restore the << tags as < tags now...
        $text = str_replace(chr(1), '<<', $text);
        // $text = str_replace(chr(255), '<', $text);

        // Get color parameters into a more useable format.
        $text = preg_replace('/<color><param>([\da-fA-F]+),([\da-fA-F]+),([\da-fA-F]+)<\/param>/Uis', '<color r=\1 g=\2 b=\3>', $text);
        $text = preg_replace('/<color><param>(red|blue|green|yellow|cyan|magenta|black|white)<\/param>/Uis', '<color n=\1>', $text);

        // Get font family parameters into a more useable format.
        $text = preg_replace('/<fontfamily><param>(\w+)<\/param>/Uis', '<fontfamily f=\1>', $text);

        // Just remove any remaining parameters -- we won't use
        // them. Any tags with parameters that we want to implement
        // will have to come before this Someday we hope to use these
        // tags (e.g. for <color><param> tags)
        $text = preg_replace('/<param>.*<\/param>/Uis', '', $text);

        // Single line breaks become spaces, double line breaks are a
        // real break. This needs to do <nofill> tracking to be
        // compliant but we don't want to deal with state at this
        // time, so we fake it some day we should rewrite this to
        // handle <nofill> correctly.
        $text = preg_replace('/([^\n])\r\n([^\r])/', '\1 \2', $text);
        $text = preg_replace('/(\r\n)\r\n/', '\1', $text);

        // We try to protect against bad stuff here.
        $text = @htmlspecialchars($text, ENT_QUOTES, $charset);

        // Now convert the known tags to html. Try to remove any tag
        // parameters to stop people from trying to pull a fast one
        $text = preg_replace('/(?<!&lt;)&lt;bold.*&gt;(.*)&lt;\/bold&gt;/Uis', '<span style="font-weight: bold">\1</span>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;italic.*&gt;(.*)&lt;\/italic&gt;/Uis', '<span style="font-style: italic">\1</span>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;underline.*&gt;(.*)&lt;\/underline&gt;/Uis', '<span style="text-decoration: underline">\1</span>', $text);
        $text = preg_replace_callback('/(?<!&lt;)&lt;color r=([\da-fA-F]+) g=([\da-fA-F]+) b=([\da-fA-F]+)&gt;(.*)&lt;\/color&gt;/Uis', array($this, 'colorize'), $text);
        $text = preg_replace('/(?<!&lt;)&lt;color n=(red|blue|green|yellow|cyan|magenta|black|white)&gt;(.*)&lt;\/color&gt;/Uis', '<span style="color: \1">\2</span>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;fontfamily f=(\w+)&gt;(.*)&lt;\/fontfamily&gt;/Uis', '<span style="font-family: \1">\2</span>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;smaller.*&gt;/Uis', '<span style="font-size: smaller">', $text);
        $text = preg_replace('/(?<!&lt;)&lt;\/smaller&gt;/Uis', '</span>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;bigger.*&gt;/Uis', '<span style="font-size: larger">', $text);
        $text = preg_replace('/(?<!&lt;)&lt;\/bigger&gt;/Uis', '</span>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;fixed.*&gt;(.*)&lt;\/fixed&gt;/Uis', '<font face="fixed">\1</font>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;center.*&gt;(.*)&lt;\/center&gt;/Uis', '<div align="center">\1</div>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;flushleft.*&gt;(.*)&lt;\/flushleft&gt;/Uis', '<div align="left">\1</div>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;flushright.*&gt;(.*)&lt;\/flushright&gt;/Uis', '<div align="right">\1</div>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;flushboth.*&gt;(.*)&lt;\/flushboth&gt;/Uis', '<div align="justify">\1</div>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;paraindent.*&gt;(.*)&lt;\/paraindent&gt;/Uis', '<blockquote>\1</blockquote>', $text);
        $text = preg_replace('/(?<!&lt;)&lt;excerpt.*&gt;(.*)&lt;\/excerpt&gt;/Uis', '<blockquote>\1</blockquote>', $text);

        // replace << with < now (from translated html form)
        $text = str_replace('&lt;&lt;', '&lt;', $text);

        // Now we remove the leading/trailing space we added at the
        // start.
        $text = preg_replace('/^ (.*) $/s', '\1', $text);

        // Wordwrap -- note this could impact on our above RFC
        // compliance *IF* we honored nofill tags (which we don't
        // yet).
        $text = str_replace("\t", '        ', $text);
        $text = str_replace('  ', ' &nbsp;', $text);
        $text = str_replace("\n ", "\n&nbsp;", $text);
        if ($text[0] == ' ') {
            $text = '&nbsp;' . substr($text, 1);
        }
        $text = nl2br($text);
        $text = '<p class="fixed">' . $text . '</p>';

        // highlight quoted parts of an email
        if ($prefs->getValue('highlight_text')) {
            $text = implode("\r\n", preg_replace('|^(\s*&gt;.+)$|', '<span class="quoted1">\1</span>', explode("\r\n", $text)));
            $indent = 1;
            while (preg_match('|&gt;(\s?&gt;){' . $indent . '}|', $text)) {
                $text = implode("\r\n", preg_replace('|^<span class="quoted' . ((($indent - 1) % 5) + 1) . '">(\s*&gt;(\s?&gt;){' . $indent . '}.+)$|', '<span class="quoted' . (($indent % 5) + 1) . '">\1', explode("\r\n", $text)));
                $indent++;
            }
        }

        // dim signatures
        if ($prefs->getValue('dim_signature')) {
            $parts = preg_split('|(\r\n--\s*\r\n)|', $text, 2, PREG_SPLIT_DELIM_CAPTURE);
            $text = array_shift($parts);
            if (count($parts)) {
                $text .= '<span class="signature">' . $parts[0];
                $text .= preg_replace('|class="[^"]+"|', 'class="signature-fixed"', $parts[1]);
                $text .= '</span>';
            }
        }

        // filter bad language
        if ($prefs->getValue('filtering')) {
            include_once HORDE_BASE . '/lib/Text.php';
            $text = Text::filter($text, $conf['msg']['filtering']['words'],
                                 $conf['msg']['filtering']['replacement']);
        }

        /* If the text is in a different character set, and we're not
           already in a new window, give the user a link to open it in
           a new window with the right character set. */
        // NOTE: This could affect RFC compliance if we implement <lang> tags
        if (strtolower($mime->charset) != strtolower(Lang::getCharset()) &&
            strtolower($mime->charset) != 'us-ascii' &&
            !strstr($_SERVER['PHP_SELF'], 'view.php')) {
            include_once HORDE_BASE . '/lib/SessionCache.php';

            $text = sprintf(_("<p><i>This message was written in a character set other than your own. If it is not displayed correctly, %sclick here</a> to open it in a new window.</i></p>\n"),
                            Horde::link('', sprintf(_("View %s"), $mime->description), null, null, "view('" . VIEW_ATTACH . "', '" . $mime->imap_id . "', '" . SessionCache::putObject($mime) . "'); return false;")) . $text;
        }

        return $text;
    }

    function colorize($colors)
    {
        for ($i = 1; $i < 4; $i++) {
            $colors[$i] = sprintf('%02X', round(hexdec($colors[$i]) / 255));
        }
        return '<span style="color: #' . $colors[1] . $colors[2] . $colors[3] . '">' . $colors[4] . '</span>';
    }

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

    function getCharset($mime)
    {
        return !empty($mime->charset) ? $mime->charset : $GLOBALS['registry']->getCharset();
    }

}
