<?php

function enriched_to_html($dsp)
{
    // Convert to HMTL
    /* Double << is <. Using &lt; makes sure it is no longer taken
       as a command start, as & will become $amp; Later we replace 
       &amp;lt; with &lt; */ 
    $dsp = str_replace("<<", "&lt;", $dsp);
    $dsp = htmlspecialchars($dsp);
    // Simple ones
    $dsp = eregi_replace("&lt;bold&gt;", "<b>", $dsp);
    $dsp = eregi_replace("&lt;/bold&gt;", "</b>", $dsp);
    $dsp = eregi_replace("&lt;italic&gt;", "<i>", $dsp);
    $dsp = eregi_replace("&lt;/italic&gt;", "</i>", $dsp);
    $dsp = eregi_replace("&lt;underline&gt;", "<u>", $dsp);
    $dsp = eregi_replace("&lt;/underline&gt;", "</u>", $dsp);
    $dsp = eregi_replace("&lt;fixed&gt;", "<tt>", $dsp);
    $dsp = eregi_replace("&lt;/fixed&gt;", "</tt>", $dsp);
    $dsp = eregi_replace("&lt;smaller&gt;", "<small>", $dsp);
    $dsp = eregi_replace("&lt;/smaller&gt;", "</small>", $dsp);
    $dsp = eregi_replace("&lt;bigger&gt;", "<big>", $dsp);
    $dsp = eregi_replace("&lt;/bigger&gt;", "</big>", $dsp);
    $dsp = eregi_replace("&lt;center&gt;", "<div align=\"center\">", $dsp);
    $dsp = eregi_replace("&lt;/center&gt;", "</div>", $dsp);
    $dsp = eregi_replace("&lt;flushleft&gt;", "<div align=\"left\">", $dsp);
    $dsp = eregi_replace("&lt;/flushleft&gt;", "</div>", $dsp);
    $dsp = eregi_replace("&lt;flushright&gt;", "<div align=\"right\">", $dsp);
    $dsp = eregi_replace("&lt;/flushright&gt;", "</div>", $dsp);
    $dsp = eregi_replace("&lt;flushboth&gt;", "<div align=\"justify\">", $dsp);
    $dsp = eregi_replace("&lt;/flushboth&gt;", "</div>", $dsp);
    /* nofill -> pre. TODO: Within this block, no newline replacing 
       should be done. */
    $dsp = eregi_replace("&lt;nofill&gt;", "<pre>", $dsp);
    $dsp = eregi_replace("&lt;/nofill&gt;", "</pre>", $dsp);
    /* Paraindent -> blockquote
       Not entriely correct: there is a param telling what kind
       of indenting to do, which we don't support.
       Another thing: My params cannot contain an & char, which
       might be a problem.
     */
    $dsp = eregi_replace("&lt;paraindent&gt;&lt;param&gt;[^&]*&lt;/param&gt;", "<blockquote>", $dsp);
    $dsp = eregi_replace("&lt;/paraindent&gt;", "</blockquote>", $dsp);
    /* Excerpt can optionally have a param telling where excerpt 
       comes from. */
    $dsp = eregi_replace("&lt;excerpt&gt;&lt;param&gt;([^&]*)&lt;/param&gt;", "<hr><br>\\\\1<blockquote>", $dsp);
    $dsp = eregi_replace("&lt;excerpt&gt;", "<hr><blockquote>", $dsp);
    $dsp = eregi_replace("&lt;/excerpt&gt;", "</blockquote>", $dsp);
    /* Lang: Language is ignored */
    $dsp = eregi_replace("&lt;lang&gt;&lt;param&gt;[^&]*&lt;/param&gt;", "", $dsp);
    $dsp = eregi_replace("&lt;/lang&gt;", "", $dsp);
    /* FontFamily */
    $dsp = eregi_replace("&lt;fontfamily&gt;&lt;param&gt;([^&]*)&lt;/param&gt;", "<font face=\"\\\\1\">", $dsp);
    $dsp = eregi_replace("&lt;/fontfamily&gt;", "</font>", $dsp);
    /* color */
    while (eregi("&lt;color&gt;&lt;param&gt;([^&]*)&lt;/param&gt;", $dsp, $regs))
    {
        // regs[1] = xxxx,yyyy,zzzz or colorname
        $c = explode(",", $regs[1]);
        if (count($c) == 1)
            $color = $c[0];
        else
        {
            // First 2 chars of 4char hex string is string /256
            $color = "#" . 
                substr($c[0], 0, 2) . 
                substr($c[1], 0, 2) . 
                substr($c[2], 0, 2);
        }
        $dsp = eregi_replace("&lt;color&gt;&lt;param&gt;[^&]*&lt;/param&gt;", "<font color=\"$color\">", $dsp);
    }
    $dsp = eregi_replace("&lt;/color&gt;", "</font>", $dsp);
    /* Now remove any unrecognised <param>s */
    $dsp = eregi_replace("&lt;param&gt;[^&]*&lt;/param&gt;", "", $dsp);
    /* And last, remove any unknown tags */
    $dsp = eregi_replace("&lt;[^&]*&gt;", "", $dsp);
    /* Now finish the work on << */
    $dsp = eregi_replace("&amp;lt;", "&lt;", $dsp);
    /* Do the newlines */
    while (ereg("\n\n", $dsp))
        $dsp = ereg_replace("\n\n", "<br>\n", $dsp);
    while (ereg("\r\n\r\n", $dsp))
        $dsp = ereg_replace("\r\n\r\n", "<br>\r\n", $dsp);
    /* text/enriched must start with filled text according to RFC */
    $dsp = "<div align=\"justify\">$dsp</div>";
    return $dsp;
}

function make_safe_html($dsp)
{
    $hidetag = array (
        '!doctype',
        'html',
        'head',
        'meta',
        'body'
    );
    foreach ($hidetag as $tag)
    {
        $dsp = eregi_replace("<\\s*(/?)\\s*$tag([^>]*)>",
                             "<!-- \\1$tag\\2 -->", $dsp);
    }
    $hideelt = array (
        'title',
        'style'
    );
    foreach ($hideelt as $tag)
    {
        $dsp = eregi_replace("<\\s*$tag([^>]*)>",
                             "<!-- $tag\\1>", $dsp);
        $dsp = eregi_replace("<\\s*/\\s*$tag([^>]*)>",
                             "</$tag\\1 -->", $dsp);
    }
    return $dsp;
}

function do_inline_display($msgnum, $part_no, $encoding, $charset, $type, $safe = 0)
{
    global $mailbox, $folder;

    /* $safe means: Safe to display in an HTML form. 
       not safe is largely unchanged. */
    $dsp = imap_fetchbody($mailbox, $msgnum, $part_no, FT_UID);
    if ($encoding == "base64") {
        $dsp = imap_base64($dsp);
    }
    if ($encoding == "qprint") {
        $dsp = my_qprint($dsp);
    }
    if (!$safe)
    {
        /* Done, no further processing. */
        echo $dsp;
    }
    else
    {
        $dsp = make_utf8($dsp, $charset);
        if (strtolower($type) == "text/plain")
        {
            $dsp = htmlspecialchars($dsp);
            if (MESSAGE_COLORS)
            {
                // from level 1 to deepest
                $quotecolors = explode(":", MESSAGE_COLORS);
                $lines = split("\r|\n|\r\n", $dsp);
                $dsp = "";
                foreach ($lines as $line)
                {
                    for ($c = count($quotecolors) - 1; $c > 0; $c--)
                    {
                        $line = ereg_replace(
                            "^(" . str_repeat("\\&gt;[ ]?", $c) . ".*)$",
                            "<font color=\"" . $quotecolors[$c] . "\">\\1</font>",
                            $line);
                    }
                    $dsp .= $line . "\r\n";
                }
                $dsp = ereg_replace(
                        "(\r\n-- \r\n.*$)",
                        "<font color=\"" . $quotecolors[0] . "\">\\1</font>",
                        $dsp);
                            
            }
            $dsp = make_clickable($dsp);
            /* For text/plain the user should take care of line break.
               If they didn't however, we do it...
             */
            if (FORCE_WRAP)
                echo "<tt>" . nl2br($dsp) . "</tt>";
            else
                echo "<pre>$dsp</pre>";
        }
        else if (strtolower($type) == "text/enriched")
        {
            $dsp = enriched_to_html($dsp);
            echo $dsp;
        }
        else if (strtolower($type) == "text/html")
        {
            while (ereg("cid:([^\"]*)", $dsp, $regs))
            {
                // Do this only if necessary, so in the while!
                if (!isset($struct))
                    $struct = imap_fetchstructure($mailbox, $msgnum, FT_UID);
               /* WARNING: MAKE SURE you quotemeta() on $regs[1]. They can
                  (and often do) contain $ characters.
               */
                $dsp = ereg_replace("cid:" . quotemeta($regs[1]),
                        cid_name($msgnum, $struct, $regs[1]), $dsp);
            }
            /* Found in the wild: uppercase CID:.... */
            while (ereg("CID:([^\"]*)", $dsp, $regs))
            {
                // Do this only if necessary, so in the while!
                if (!isset($struct))
                    $struct = imap_fetchstructure($mailbox, $msgnum, FT_UID);
               /* WARNING: MAKE SURE you quotemeta() on $regs[1]. They can
                  (and often do) contain $ characters.
               */
                $dsp = ereg_replace("CID:" . quotemeta($regs[1]),
                        cid_name($msgnum, $struct, $regs[1]), $dsp);
            }
            $dsp = make_safe_html($dsp);
            echo $dsp;
        }
        else
        {
            /* For unknown types, we just display exactly as sent, so here
               we always use <pre>. */
            $dsp = htmlspecialchars($dsp);
            echo "<pre>\n$dsp</pre>\n";
        }
    }
}

function get_mime_type($de_part)
{
    switch ($de_part->type)
    {
        case TYPETEXT:      $mime_type = "text"; break;
        case TYPEMULTIPART: $mime_type = "multipart"; break;
        case TYPEMESSAGE:   $mime_type = "message"; break;
        case TYPEAPPLICATION:   $mime_type = "application"; break;
        case TYPEAUDIO:     $mime_type = "audio"; break;
        case TYPEIMAGE:     $mime_type = "image"; break;
        case TYPEVIDEO:     $mime_type = "video"; break;
        case TYPEMODEL:     $mime_type = "model"; break;
        default:        $mime_type = "unknown";
    }
    return $mime_type;
}

function inline_display($msgnum, $de_part, $part_no)
{
    global $mailbox, $folder;
    
    
    $mime_type = get_mime_type($de_part);
    $type = $mime_type . "/" . $de_part->subtype;
    $mime_encoding = get_mime_encoding($de_part);
    $charset = get_charset($de_part);

    /* Default, simple mail */
    if (strtolower($type) == "text/plain")
    {
        $ret = 1;
    }
    /* Smart types we know of */
    elseif ((strtolower($type) == "text/enriched") or
            (strtolower($type) == "text/html"))
    {
        output_bound(L_MIME_SECTION, "$mime_type/$de_part->subtype");
        $ret = 1;
    }
    /* All other types: return 0 as we don't know whether we displayed 
       anything useful for the user. */
    else
    {
        output_bound(L_MIME_SECTION, "$mime_type/$de_part->subtype");
        $ret = 0;
    }
    /* This is not really useful
    print "<p><a class=\"intf\" href=\"view_inline.php?folder=" . urlencode($folder) 
                . "&amp;msgnum=$msgnum"
                . "&amp;part_no=$part_no"
                . "&amp;encoding=" . urlencode($mime_encoding)
                . "&amp;charset=" . urlencode($charset)
                . "&amp;type=" . urlencode($type)
                . "\">" . L_MESSAGE . "</a></p>\n";
    */                
    
    /* Embedded objects have a fixed height, which means embedded scrolbars.
       That's not worth it.
    print "<object "
                . "data=\"view_inline.php?folder=" . urlencode($folder) 
                . "&amp;msgnum=$msgnum"
                . "&amp;part_no=$part_no"
                . "&amp;encoding=" . urlencode($mime_encoding)
                . "&amp;charset=" . urlencode($charset)
                . "&amp;type=" . urlencode($type)
                . "\" width=\"100%\">\n";
    */                

    do_inline_display($msgnum, $part_no, $mime_encoding,
                      $charset, $type, 1);
    /*
    echo "</object>\n";
    */
    return $ret;
}

function output_bound($title, $str)
{
    body_left_end();
    body_row_end();

    title_start();
    title_left();
    echo $title;
    title_left_end();
    title_data();
    echo $str;
    title_data_end();
    body_row();
    body_left('colspan="2" class="body"');
}


function cid_name($msgnum, $struct, $cid_to_find, $numprefix="")
{
    global $folder;
    global $cidsdone;

    $name = "";
    $numparts = !$struct->parts ? "1" : count($struct->parts);
    for($i = 0; $i < $numparts; $i++)
    {
        $part = !$struct->parts[$i] ? $struct : $struct->parts[$i];

        if ($part->parts)
        {
            $subpartnum = $i + 1;
            $name = cid_name($msgnum, $part, $cid_to_find, $numprefix . $subpartnum . ".");
            if ($name != $cid_to_find)
            {
                return $name;
            }
        }
        else
        {
            $cid = $part->ifid ? $part->id : "";
            if ($cid == $cid_to_find || $cid == "<$cid_to_find>")
            {
                $subpartnum = $i + 1;
                $att_name = get_att_name($part);
                $name = "view_image.php?folder=" . $folder 
                    . "&amp;msgnum=$msgnum&amp;part_no=" . $numprefix
                    . $subpartnum
                    . "&amp;type=$part->subtype&amp;name=$att_name";
                $cidsdone[$cid] = 1;
                return $name;
            }
        }
    }
    // Not found, leave it as it is
    return $cid_to_find;
}

/* ciddone is set for an inline image that is processed. If the image data 
   appears later in the message, it doesn't have to be displayed again.
   This is not entirely correct, because when the image data appears first, 
   it is displayed, and again later as inline.
*/
function ciddone($part)
{
    global $cidsdone;
    $cid = $part->ifid ? $part->id : "";
    if ($cid == "")
        return 0;
    return $cidsdone[$cid];
}

function image_display($folder, $msgnum, $de_part, $part_no, $att_name)
{
    output_bound(L_IMAGE . ":" , $att_name);
    echo "\n<img src=\""; 
    echo "view_image.php?folder=" . urlencode($folder) . "&amp;msgnum=$msgnum&amp;part_no=$part_no";
    echo "&amp;type=$de_part->subtype&amp;name=$att_name\">\n<br>\n";
}

// function make_clickable rewritten to follow
// RFC 1738 (URLs)
// RFC 2822 (Internet Message formats)
function make_clickable($text)
{
    global $folder;
    
    // schemes in URLs that we will recognise. These are schemes commonly
    // supported by browsers, others are not necessary.
    $schemes = array (
        // official
        "ftp",
        "http",
        "gopher",
        "news",
        "nntp",
        "telnet",
        "wais",
        "file",
        "https",
        "tn3270",
        // common?
        "aim",
        "irc"
        // DON'T include script types for security
    );

    // Turn &amp; into st that does not contain an &, and is 
    // not appearing in text. Then the URL matcher can exclude &s; so &gt; and
    // &lt; are not matched.
    do {
        $rep = "!=!" . rand() . "!=!";
    } while (eregi($rep, $text));
    $ret = eregi_replace("&amp;", $rep, $text);
    // RFC 1738, but restrict to common schemes
    $schemere = implode("|", $schemes);
    $ret = eregi_replace(
        '(' . "($schemere)" . ':[-$_.+!*\'(),;/?:@=~[:alnum:]#]+)', 
        "<a href=\"\\1\" target=\"_new\">\\1</a>", $ret);
    // One more as it is used a lot: something without a scheme, which is 
    // always http://.
    // Try not to match too much, so demand . and /
    // make sure it is surrounded by blanks
    $ret = eregi_replace(
        '(^|[[:space:]])([[:alnum:]]+\.[[:alnum:]\.]+/[-$_.+!*\'(),;/?:@=~[:alnum:]#]+)([[:space:]]|$)',
        "\\1<a href=\"http://\\2\" target=\"_new\">\\2</a>\\3", $ret);
    $ret = eregi_replace($rep, "&amp;", $ret);
    if (WRITE_MESSAGES)
    {
        $ret = eregi_replace(
           '([-\.!#$%&\'*+/=?^_`{|}~[:alnum:]]+@[-a-zA-Z0-9.]+)',
            "<a href=\"compose.php?folder=" 
                . urlencode($folder) 
                . "&amp;to=\\1\">\\1</a>", 
            $ret);
    }
    return($ret);
}


function display_mime_part($msgnum, $struct, $numprefix="")
{
    global $folder;
    $numparts = !$struct->parts ? "1" : count($struct->parts);
    echo "<!-- This message has " . $numparts . " part(s) -->\n";

     /* If multipart/alternative, display the best one. This is
        the last one we can handle.
        This is not entirely correct, as we will display later ones even
        if we don't know how to handle it.
    */
    $alternative = 0;
    if (get_mime_type($struct) == "multipart" 
        && strtoupper($struct->subtype) == "ALTERNATIVE")
    {
        $alternative = 1;
    }
    for($i = 0; $i < $numparts; $i++)
    {
        /* If multipart/alternative, last one first */
        if ($alternative)
            $nr = $numparts - $i - 1;
        else
            $nr = $i;
        $part = !$struct->parts[$nr] ?$part = $struct : $part = $struct->parts[$nr];

        if ($part->parts)
        {
            $subpartnum = $nr + 1;
            display_mime_part($msgnum, $part, $numprefix . $subpartnum . ".");
        }
        else
        {

            $att_name = get_att_name($part);
            if ($att_name == "Unknown")
            {
                $subpartnum = $nr + 1;
                $nicelydisplayed = inline_display($msgnum, $part, $numprefix . $subpartnum);
                echo "\n<br>";
            }
            /* If this picture is used as in in-line picture, there is no
               use displaying it again. */
            
            if (!ciddone($part))
            {
                // we want to display images here, even though they are
                // attachments.
                $mime_encoding = get_mime_encoding($part);
                $mime_type = get_mime_type($part);
                if(($mime_encoding == "base64") and ($mime_type == "image"))
                {
                    $subpartnum = $nr + 1;
                    echo "<p>" . image_display($folder, $msgnum, $part, $numprefix . $subpartnum, $att_name) . "</p>\n";
                }
            }
        }
        /* If multipart/alternative, as soon as it is displayed we're done */
        if ($alternative && $nicelydisplayed)
            break;
    }
}

function attach_display_string($msgnum, $de_part, $part_no)
{
    global $mailbox, $folder;
    $mime_type = get_mime_type($de_part);
    $mime_encoding = get_mime_encoding($de_part);

    $att_name = get_att_name($de_part);
    $url_att_name = urlencode($att_name);

    $ret = "<a class=\"intf\" href=\"get_attach.php?folder=$folder&amp;msgnum=$msgnum&amp;part_no=$part_no";
    $ret .= "&amp;type=$mime_type&amp;subtype=$de_part->subtype&amp;name=$url_att_name&amp;encoding=$mime_encoding";
    $ret .= "\">$att_name</a>";
    return $ret;
}


function get_attach_display_list(&$att_list, $msgnum, $struct)
{
    if($struct->parts)
    {
        for($j = 0; $j < count($struct->parts); $j++)
        {
            do_get_attach_display_list($att_list, $msgnum,
                    $struct->parts[$j], $j, "");
        }
    }
    else // the struct IS the part (which will never be an attachment...
    {
        do_get_attach_display_list($att_list, $msgnum,
                $struct->parts[$j], $j, "");
    }
        
}


function do_get_attach_display_list(&$att_list, $msgnum, $struct, $nr, $numprefix)
{
    $subpartnum = $nr + 1;

    // If this is a leaf, investigate it
    if(!$struct->parts)
    {
        $att_name = get_att_name($struct);
        if ($att_name != "Unknown")
        {
            // if it has a name, it's an attachment
            $disp = attach_display_string($msgnum, $struct, $numprefix . $subpartnum);
            $att_list[] = $disp;
        }
    }
    else // If not a leaf, traverse its children
    {
        for($j = 0; $j < count($struct->parts); $j++)
        {
            do_get_attach_display_list($att_list, $msgnum, $struct->parts[$j], $j, 
                    $numprefix . $subpartnum . ".");
        }
    }
}



?>
