steffen: server/kolab-horde-framework/kolab-horde-framework/MIME/MIME/Viewer deb.php, NONE, 1.1 default.php, NONE, 1.1 enriched.php, NONE, 1.1 enscript.php, NONE, 1.1 html.php, NONE, 1.1 icalendar.php, NONE, 1.1 images.php, NONE, 1.1 msexcel.php, NONE, 1.1 mspowerpoint.php, NONE, 1.1 msword.php, NONE, 1.1 ooo.php, NONE, 1.1 pdf.php, NONE, 1.1 php.php, NONE, 1.1 plain.php, NONE, 1.1 rar.php, NONE, 1.1 report.php, NONE, 1.1 rfc822.php, NONE, 1.1 richtext.php, NONE, 1.1 rpm.php, NONE, 1.1 security.php, NONE, 1.1 source.php, NONE, 1.1 srchighlite.php, NONE, 1.1 tgz.php, NONE, 1.1 tnef.php, NONE, 1.1 vcard.php, NONE, 1.1 webcpp.php, NONE, 1.1 zip.php, NONE, 1.1

cvs at intevation.de cvs at intevation.de
Fri Oct 14 16:33:09 CEST 2005


Author: steffen

Update of /kolabrepository/server/kolab-horde-framework/kolab-horde-framework/MIME/MIME/Viewer
In directory doto:/tmp/cvs-serv28903/kolab-horde-framework/kolab-horde-framework/MIME/MIME/Viewer

Added Files:
	deb.php default.php enriched.php enscript.php html.php 
	icalendar.php images.php msexcel.php mspowerpoint.php 
	msword.php ooo.php pdf.php php.php plain.php rar.php 
	report.php rfc822.php richtext.php rpm.php security.php 
	source.php srchighlite.php tgz.php tnef.php vcard.php 
	webcpp.php zip.php 
Log Message:
Separated Horde Framework from kolab-resource-handlers

--- NEW FILE: deb.php ---
<?php
/**
 * The MIME_Viewer_deb class renders out lists of files in Debian
 * packages by using the dpkg tool to query the package.
 *
 * $Horde: framework/MIME/MIME/Viewer/deb.php,v 1.11 2004/04/16 17:21:45 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_deb extends MIME_Viewer {

    /**
     * Render the data.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the viewer may need.
     *
     * @return string  The rendered data.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['deb']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['deb']['location']) . '</pre>';
        }

        $tmp_deb = Horde::getTempFile('horde_deb');

        $fh = fopen($tmp_deb, 'w');
        fwrite($fh, $this->mime_part->getContents());
        fclose($fh);

        $fh = popen($mime_drivers['horde']['deb']['location'] . " -f $tmp_deb 2>&1", 'r');
        while (($rc = fgets($fh, 8192))) {
            $data .= $rc;
        }
        pclose($fh);

        return '<pre>' . htmlentities($data) . '</pre>';
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output.
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: default.php ---
<?php
/**
 * The MIME_Viewer_default class simply prints out the encapsulated
 * content.  It exists as a fallback if no other intelligent rendering
 * mechanism could be used.
 *
 * $Horde: framework/MIME/MIME/Viewer/default.php,v 1.9 2004/01/01 15:14:20 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_default extends MIME_Viewer {

}

--- NEW FILE: enriched.php ---
<?php
/**
 * The MIME_Viewer_enriched class renders out plain text from
 * enriched content tags, ala RFC 1896.
 *
 * 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.
 *
 * $Horde: framework/MIME/MIME/Viewer/enriched.php,v 1.21 2004/04/13 18:03:51 slusarz Exp $
 *
 * Copyright 2001-2004 Eric Rostetter <eric.rostetter at physics.utexas.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Eric Rostetter <eric.rostetter at physics.utexas.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 2.1
 * @package Horde_MIME_Viewer
 */
class 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()
    {
        if (($text = $this->mime_part->getContents()) === false) {
            return false;
        }

        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 . ' ';

        // 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);

        // 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, $this->mime_part->getCharset());

        // 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('/(?<!<)<bold.*>(.*)<\/bold>/Uis', '<span style="font-weight: bold">\1</span>', $text);
        $text = preg_replace('/(?<!<)<italic.*>(.*)<\/italic>/Uis', '<span style="font-style: italic">\1</span>', $text);
        $text = preg_replace('/(?<!<)<underline.*>(.*)<\/underline>/Uis', '<span style="text-decoration: underline">\1</span>', $text);
        $text = preg_replace_callback('/(?<!<)<color r=([\da-fA-F]+) g=([\da-fA-F]+) b=([\da-fA-F]+)>(.*)<\/color>/Uis', array($this, 'colorize'), $text);
        $text = preg_replace('/(?<!<)<color n=(red|blue|green|yellow|cyan|magenta|black|white)>(.*)<\/color>/Uis', '<span style="color: \1">\2</span>', $text);
        $text = preg_replace('/(?<!<)<fontfamily>(.*)<\/fontfamily>/Uis', '\1', $text);
        $text = preg_replace('/(?<!<)<fontfamily f=(\w+)>(.*)<\/fontfamily>/Uis', '<span style="font-family: \1">\2</span>', $text);
        $text = preg_replace('/(?<!<)<smaller.*>/Uis', '<span style="font-size: smaller">', $text);
        $text = preg_replace('/(?<!<)<\/smaller>/Uis', '</span>', $text);
        $text = preg_replace('/(?<!<)<bigger.*>/Uis', '<span style="font-size: larger">', $text);
        $text = preg_replace('/(?<!<)<\/bigger>/Uis', '</span>', $text);
        $text = preg_replace('/(?<!<)<fixed.*>(.*)<\/fixed>/Uis', '<font face="fixed">\1</font>', $text);
        $text = preg_replace('/(?<!<)<center.*>(.*)<\/center>/Uis', '<div align="center">\1</div>', $text);
        $text = preg_replace('/(?<!<)<flushleft.*>(.*)<\/flushleft>/Uis', '<div align="left">\1</div>', $text);
        $text = preg_replace('/(?<!<)<flushright.*>(.*)<\/flushright>/Uis', '<div align="right">\1</div>', $text);
        $text = preg_replace('/(?<!<)<flushboth.*>(.*)<\/flushboth>/Uis', '<div align="justify">\1</div>', $text);
        $text = preg_replace('/(?<!<)<paraindent.*>(.*)<\/paraindent>/Uis', '<blockquote>\1</blockquote>', $text);
        $text = preg_replace('/(?<!<)<excerpt.*>(.*)<\/excerpt>/Uis', '<blockquote>\1</blockquote>', $text);

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

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

        // Make URLs clickable.
        require_once 'Horde/Text.php';
        $text = Text::linkUrls($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('  ', '  ', $text);
        $text = str_replace("\n ", "\n ", $text);
        if ($text[0] == ' ') {
            $text = ' ' . substr($text, 1);
        }
        $text = nl2br($text);
        $text = '<p class="fixed">' . $text . '</p>';

        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 the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: enscript.php ---
<?php

require_once dirname(__FILE__) . '/source.php';

/**
 * The MIME_Viewer_enscript class renders out various content
 * in HTML format by using GNU Enscript.
 *
 * $Horde: framework/MIME/MIME/Viewer/enscript.php,v 1.38 2004/05/22 03:06:23 chuck Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_enscript extends MIME_Viewer_source {

    /**
     * Render out the data using Enscript.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered data.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['enscript']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['enscript']['location']) . '</pre>';
        }

        /* Create temporary files for input to Enscript. Note that we
           cannot use a pipe, since enscript must have access to the
           whole file to determine its type for coloured syntax
           highlighting. */
        $tmpin = Horde::getTempFile('EnscriptIn');

        /* Write the contents of our buffer to the temporary input file. */
        $contents = $this->mime_part->getContents();
        $fh = fopen($tmpin, 'wb');
        fwrite($fh, $contents, strlen($contents));
        fclose($fh);

        /* Execute the enscript command */
        $lang = escapeshellarg($this->_typeToLang($this->mime_part->getType()));
        $results = shell_exec($mime_drivers['horde']['enscript']['location'] . " -E$lang --language=html --color --output=- < $tmpin");

        /* Strip out the extraneous HTML from Enscript, and output it. */
        $res_arr = preg_split('/\<\/?pre\>/i', $results);
        if (count($res_arr) == 3) {
            $results = '<span style="white-space:pre;font-family:monospace">' . trim($res_arr[1]) . '</span>';
        }

        return $this->lineNumber($results);
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output.
     */
    function getType()
    {
        return 'text/html';
    }

    /**
     * Attempts to determine what language to use for the enscript program
     * from a MIME type.
     *
     * @access private
     *
     * @param string $type  The MIME type.
     *
     * @return string  The enscript 'language' parameter string.
     */
    function _typeToLang($type)
    {
        include_once dirname(__FILE__) . '/../Magic.php';

        $ext = MIME_Magic::MIMEToExt($type);

        switch ($ext) {
        case 'cs':
            return 'java';

        case 'el':
            return 'elisp';

        case 'h':
            return 'c';

        case 'C':
        case 'H':
        case 'cc':
        case 'hh':
        case 'c++':
        case 'cxx':
        case 'cpp':
            return 'cpp';

        case 'htm':
        case 'shtml':
        case 'xml':
            return 'html';

        case 'js':
            return 'javascript';

        case 'pas':
            return 'pascal';

        case 'al':
        case 'pl':
        case 'pm':
            return 'perl';

        case 'ps':
            return 'postscript';

        case 'vb':
            return 'vba';

        case 'vhd':
            return 'vhdl';

        case 'patch':
        case 'diff':
            return 'diffu';

        default:
            return $ext;
        }
    }

}

--- NEW FILE: html.php ---
<?php
/**
 * The MIME_Viewer_html class renders out HTML text with an effort to
 * remove potentially malicious code.
 *
 * $Horde: framework/MIME/MIME/Viewer/html.php,v 1.5 2004/04/24 13:55:11 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 * Copyright 1999-2004 Jon Parise <jon at horde.org>
 * Copyright 2002-2004 Michael Slusarz <slusarz at horde.org>
 *
 * 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  Anil Madhavapeddy <anil at recoil.org>
 * @author  Jon Parise <jon at horde.org>
 * @author  Michael Slusarz <slusarz at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Mime_Viewer
 */
class MIME_Viewer_html extends MIME_Viewer {

    /**
     * Render out the currently set contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the viewer may need.
     *
     * @return string  The rendered text.
     */
    function render($params = null)
    {
        return $this->_cleanHTML($this->mime_part->getContents());
    }

    /**
     * These regular expressions attempt to make HTML safe for
     * viewing. THEY ARE NOT PERFECT. If you enable HTML viewing,
     * you are opening a security hole. With the current state of
     * the web, I believe that the best we can do is to make sure
     * that people *KNOW* HTML is a security hole, clean up what
     * we can, and leave it at that.
     *
     * @access private
     *
     * @param string $data  The HTML data.
     *
     * @return string  The cleaned HTML data.
     */
    function _cleanHTML($data)
    {
        global $browser, $prefs;

        require_once 'Horde/MIME/Contents.php';
        $attachment = MIME_Contents::viewAsAttachment();

        /* Deal with <base> tags in the HTML, since they will screw up our
           own relative paths. */
        if (($i = stristr($data, '<base ')) && ($i = stristr($i, 'http')) &&
            ($j = strchr($i, '>'))) {
            $base = substr($i, 0, strlen($i) - strlen($j));
            $base = preg_replace('|(http.*://[^/]*/?).*|i', '\1', $base);

            if ($base[strlen($base) - 1] != '/') {
                $base .= '/';
            }
        }

        /* Change space entities to space characters. */
        $data = preg_replace('/&#(x0*20|0*32);?/i', ' ', $data);

        /* Nuke non-printable characters (a play in three acts). */

        /* Rule #1: If we have a semicolon, it is deterministically
         * detectable and fixable, without introducing collateral damage. */
        $data = preg_replace('/&#x?0*([9A-D]|1[0-3]);/i', ' ', $data);

        /* Rule #2: Hex numbers (usually having an x prefix) are also
         * deterministic, even if we don't have the semi. Note that
         * some browsers will treat &#a or &#0a as a hex number even
         * without the x prefix; hence /x?/ which will cover those
         * cases in this rule. */
        $data = preg_replace('/&#x?0*[9A-D]([^0-9A-F]|$)/i', '&nbsp\\1', $data);

        /* Rule #3: Decimal numbers without semi. The problem is that
         * some browsers will interpret &#10a as "\na", some as
         * "&#x10a" so we have to clean the &#10 to be safe for the
         * "\na" case at the expense of mangling a valid entity in
         * other cases. (Solution for valid HTML authors: always use
         * the semicolon.) */
        $data = preg_replace('/&#0*(9|1[0-3])([^0-9]|$)/i', '&nbsp\\2', $data);

        /* Remove overly long numeric entities. */
        $data = preg_replace('/&#x?0*[0-9A-F]{6,};?/i', ' ', $data);

        /* Remove everything outside of and including the <body> tag if
         * displaying inline. */
        if (!$attachment) {
            $data = preg_replace('/.*<body[^>]*>/si', '', $data);
            $data = preg_replace('/<\/body>.*/si', '', $data);
        }

        /* Get all attribute="javascript:foo()" tags. This is essentially
         * the regex /(=|url\()("?)[^>]*script:/ but expanded to catch
         * camouflage with spaces and entities. */
        $preg = '/((&#0*61;?|&#x0*3D;?|=)|' .
                '((u|&#0*85;?|&#x0*55;?|&#0*117;?|&#x0*75;?)\s*' .
                '(r|&#0*82;?|&#x0*52;?|&#0*114;?|&#x0*72;?)\s*' .
                '(l|&#0*76;?|&#x0*4c;?|&#0*108;?|&#x0*6c;?)\s*' .
                '(\()))\s*' .
                '(&#0*34;?|&#x0*22;?|"|&#0*39;?|&#x0*27;?|\')?' .
                '[^>]*\s*' .
                '(s|&#0*83;?|&#x0*53;?|&#0*115;?|&#x0*73;?)\s*' .
                '(c|&#0*67;?|&#x0*43;?|&#0*99;?|&#x0*63;?)\s*' .
                '(r|&#0*82;?|&#x0*52;?|&#0*114;?|&#x0*72;?)\s*' .
                '(i|&#0*73;?|&#x0*49;?|&#0*105;?|&#x0*69;?)\s*' .
                '(p|&#0*80;?|&#x0*50;?|&#0*112;?|&#x0*70;?)\s*' .
                '(t|&#0*84;?|&#x0*54;?|&#0*116;?|&#x0*74;?)\s*' .
                '(:|&#0*58;?|&#x0*3a;?)/i';
        $data = preg_replace($preg, '\1\8HordeCleaned', $data);

        /* Get all on<foo>="bar()". NEVER allow these. */
        $data = preg_replace('/(\s+[Oo][Nn]\w+)\s*=/', '\1HordeCleaned=', $data);

        /* Get all tags that might cause trouble - <object>, <embed>,
         * <base>, etc. Meta refreshes and iframes, too. */
        $malicious = array('|<([^>]*)s\s*c\s*r\s*i\s*p\s*t|i',
                           '|<([^>]*)embed|i',
                           '|<([^>]*)base[^line]|i',
                           '|<([^>]*)meta|i',
                           '|<([^>]*)j\sa\sv\sa|i',
                           '|<([^>]*)object|i',
                           '|<([^>]*)iframe|i');
        $data = preg_replace($malicious, '<HordeCleaned_tag', $data);

        /* Comment out style/link tags, only if we are viewing inline.
           NEVER show style tags to Netscape 4.x users since 1) the output
           will really, really suck and 2) there might be security issues. */
        if (!$attachment || 
            ($GLOBALS['browser']->isBrowser('mozilla') &&
             ($GLOBALS['browser']->getMajor() == 4))) {
            $orig_data = $data;
            $data = preg_replace('/\s+style\s*=/i', ' HordeCleaned=', $data);
            $data = preg_replace('|<style[^>]*>(?:\s*<\!--)*|i', '<!--', $data);
            $data = preg_replace('|(?:-->\s*)*</style>|i', '-->', $data);
            $data = preg_replace('|(<link[^>]*>)|i', '<!-- $1 -->', $data);
        }

        /* A few other matches. */
        $data = preg_replace('|<([^>]*)&{.*}([^>]*)>|', '<&{;}\3>', $data);
        $data = preg_replace('|<([^>]*)mocha:([^>]*)>|i', '<HordeCleaned\2>', $data);

        /* Attempt to fix paths that were relying on a <base> tag. */
        if (!empty($base)) {
            $data = preg_replace('|src="/|i', 'src="' . $base, $data);
            $data = preg_replace('|src=\'/|i', 'src=\'' . $base, $data);
            $data = preg_replace('|src=[^\'"]/|i', 'src=' . $base, $data);

            $data = preg_replace('|href= *"/|i', 'href="' . $base, $data);
            $data = preg_replace('|href= *\'/|i', 'href=\'' . $base, $data);
            $data = preg_replace('|href= *[^\'"]/|i', 'href=' . $base, $data);
        }

        return $data;
    }

    /**
     * Return the content-type of the rendered text.
     *
     * @access public
     *
     * @return string  The MIME Content-Type.
     */
    function getType()
    {
        require_once 'Horde/MIME/Contents.php';
        return (MIME_Contents::viewAsAttachment()) ? $this->mime_part->getType(true) : 'text/html';
    }

}

--- NEW FILE: icalendar.php ---
<?php
/**
 * The MIME_Viewer_icalendar class displays vCalendar/iCalendar data
 * and provides an option to import the data into a calendar source,
 * if one is available.
 *
 * $Horde: framework/MIME/MIME/Viewer/icalendar.php,v 1.41 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 2002-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @author  Mike Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_icalendar extends MIME_Viewer {

    /**
     * Messages.
     */
    var $_msgs = array();

    var $_method = 'PUBLISH';

    /**
     * Render out the currently set icalendar contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        global $registry;

        require_once 'Horde/iCalendar.php';

        // Extract the data.
        $data = $this->mime_part->getContents();

        // Parse the iCal file.
        $vCal = &new Horde_iCalendar();
        if (!$vCal->parsevCalendar($data)) {
            return sprintf('<b>%s</b><br /><pre>%s</pre>', _("The calendar data is invalid"), htmlspecialchars($data));
        }

        // Get the method type
        $this->_method = $vCal->getAttribute('METHOD');

        // Get the iCalendar file components.
        $components = $vCal->getComponents();

        // Handle the action requests
        $actions = Util::getFormData('action', array());
        foreach ($actions as $key => $action) {
            switch ($action) {
            case 'import':
                // vFreebusy reply
                // vFreebusy publish
                // vEvent request
                // vEvent publish
                // vTodo publish
                // vJournal publish
                switch ($components[$key]->getType()) {
                case 'vEvent':
                    // Import into Kronolith
                    if ($registry->hasMethod('calendar/import')) {
                        $guid = $registry->call('calendar/import', array($components[$key]));
                        if (is_a($guid, 'PEAR_Error')) {
                            $this->_msgs[$key] = array('error', sprintf(_("There was an error importing the event: %s."), $guid->getMessage()));
                        } else {
                            $url = Horde::url($registry->link('calendar/show', array('guid' => $guid)));
                            $this->_msgs[$key] = array('success', _("The event has been added to your calendar.") .
                                                       ' ' . Horde::link($url, _("View event"), null, '_blank') . Horde::img('mime/icalendar.gif', _("View event"), null, $registry->getParam('graphics', 'horde')) . '</a>');
                        }
                    } else {
                        $this->_msgs[$key] = array('warning', _("This action is not supported."));
                    }
                    break;

                case 'vFreebusy':
                    // Import into Moment.
                    if ($registry->hasMethod('calendar/import_vfreebusy')) {
                        $res = $registry->call('calendar/import_vfreebusy', array($components[$key]));
                        if (is_a($res, 'PEAR_Error')) {
                            $this->_msgs[$key] = array('error', sprintf(_("There was an error importing user's free/busy information: %s."), $res->getMessage()));
                        } else {
                            $this->_msgs[$key] = array('success', _("The user's free/busy information was sucessfully stored."));
                        }
                    } else {
                        $this->_msgs[$key] = array('warning', _("This action is not supported."));
                    }
                    break;

                case 'vTodo':
                    // Import into Nag.
                    if ($registry->hasMethod('tasks/import')) {
                        $guid = $registry->call('tasks/import', array($components[$key], 'text/x-vtodo'));
                        if (is_a($guid, 'PEAR_Error')) {
                            $this->_msgs[$key] = array('error', sprintf(_("There was an error importing the task: %s."), $test->getMessage()));
                        } else {
                            $url = Horde::url($registry->link('tasks/show', array('guid', $guid)));
                            $this->_msgs[$key] = array('success', _("The task has been added to your tasklist.") .
                                                       ' ' . Horde::link($url, _("View task"), null, '_blank') . Horde::img('mime/icalendar.gif', _("View task"), null, $registry->getParam('graphics', 'horde')) . '</a>');
                        }
                    } else {
                        $this->_msgs[$key] = array('warning', _("This action is not supported."));
                    }
                    break;

                case 'vJournal':
                default:
                    $this->_msgs[$key] = array('warning', _("This action is not yet implemented."));
                }

                break;

            case 'accept':
            case 'deny':
            case 'tentative':
                // vEvent request
                if (array_key_exists($key, $components) && $components[$key]->getType() == 'vEvent') {
                    $vEvent = $components[$key];

                    require_once 'Horde/Identity.php';
                    require_once 'Horde/MIME.php';
                    require_once 'Horde/MIME/Headers.php';
                    require_once 'Horde/MIME/Part.php';
                    require_once 'Horde/Text.php';

                    // Find out who we are and update status.
                    $identity = &Identity::singleton();
                    $email = $identity->getValue('from_addr');
                    $cn = $identity->getValue('fullname');
                    switch ($action) {
                    case 'accept':
                        $vEvent->updateAttendee($email, 'ACCEPTED', $cn);
                        break;

                    case 'deny':
                        $vEvent->updateAttendee($email, 'DECLINED', $cn);
                        break;

                    case 'tentative':
                        $vEvent->updateAttendee($email, 'TENTATIVE', $cn);
                        break;
                    }

                    // Get the organizer details
                    $organizer = parse_url($vEvent->getAttribute('ORGANIZER'));
                    $organizerEmail = $organizer['path'];
                    $organizer = $vEvent->getAttribute('ORGANIZER', true);
                    $organizerName = array_key_exists('cn', $organizer) ? $organizer['cn'] : '';

                    // Build the reply
                    $vCal = &new Horde_iCalendar();
                    $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN');
                    $vCal->setAttribute('METHOD', 'REPLY');
                    $vCal->addComponent($vEvent);

                    $mime = &new MIME_Message();
                    $message = _("Attached is an iCalendar file reply to a request you sent");
                    $body = &new MIME_Part('text/plain', Text::wrap($message, 76, "\n"));

                    $ics = &new MIME_Part('text/calendar', $vCal->exportvCalendar());
                    $ics->setName('icalendar.ics');
                    $ics->setContentTypeParameter('METHOD', 'REPLY');

                    $mime->addPart($body);
                    $mime->addPart($ics);

                    // Build the reply headers
                    $msg_headers = &new MIME_Headers();
                    $msg_headers->addReceivedHeader();
                    $msg_headers->addMessageIdHeader();
                    $msg_headers->addHeader('Date', date('r'));
                    $msg_headers->addHeader('From', $email);
                    $msg_headers->addHeader('To', $organizerEmail);

                    $identity->setDefault(Util::getFormData('identity'));
                    $replyto = $identity->getValue('replyto_addr');
                    if (!empty($replyto) && ($replyto != $barefrom)) {
                        $msg_headers->addHeader('Reply-to', $replyto);
                    }
                    $msg_headers->addHeader('Subject', sprintf(_("Reply: %s"), $vEvent->getAttribute('SUMMARY')));
                    $msg_headers->addMIMEHeaders($mime);

                    // Send the reply
                    $status = $mime->send($organizerEmail, $msg_headers);
                    if (is_a($status, 'PEAR_Error')) {
                        $this->_msgs[$key] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage()));
                    } else {
                        $this->_msgs[$key] = array('success', _("Reply Sent."));
                    }
                } else {
                    $this->_msgs[$key] = array('warning', _("This action is not supported."));
                }
                break;

            case 'send':
                // vEvent refresh
                // vTodo refresh
            case 'reply':
                // vfreebusy request
                if (array_key_exists($key, $components) && $components[$key]->getType() == 'vFreebusy') {
                    $vFb = $components[$key];
                    $startStamp = $vFb->getAttribute('DTSTART');
                    if (is_a($startStamp, 'PEAR_Error')) {
                        $startStamp = time();
                    }
                    $endStamp = $vFb->getAttribute('DTEND');
                    if (is_a($endStamp, 'PEAR_Error')) {
                        $duration = $vFb->getAttribute('DURATION');
                        if (is_a($duration, 'PEAR_Error')) {
                            $endStamp = $startStamp + (60 * 24 * 3600);
                        } else {
                            $endStamp = $startStamp + $duration;
                        }
                    }
                    $vfb_reply = $registry->call('calendar/getFreeBusy', array('startStamp' => $startStamp,
                                                                               'endStamp' => $endStamp));
                    require_once 'Horde/Identity.php';
                    require_once 'Horde/MIME.php';
                    require_once 'Horde/MIME/Headers.php';
                    require_once 'Horde/MIME/Part.php';
                    require_once 'Horde/Text.php';

                    // Find out who we are and update status.
                    $identity = &Identity::singleton();
                    $email = $identity->getValue('from_addr');
                    $cn = $identity->getValue('fullname');

                    // Get the organizer details
                    $organizer = parse_url($vFb->getAttribute('ORGANIZER'));
                    $organizerEmail = $organizer['path'];
                    $organizer = $vFb->getAttribute('ORGANIZER', true);
                    $organizerName = array_key_exists('cn', $organizer) ? $organizer['cn'] : '';

                    // Build the reply
                    $vCal = &new Horde_iCalendar();
                    $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN');
                    $vCal->setAttribute('METHOD', 'REPLY');
                    $vCal->addComponent($vfb_reply);

                    $mime = &new MIME_Message();
                    $message = _("Attached is an iCalendar file reply to a request you sent");
                    $body = &new MIME_Part('text/plain', Text::wrap($message, 76, "\n"));

                    $ics = &new MIME_Part('text/calendar', $vCal->exportvCalendar());
                    $ics->setName('icalendar.ics');
                    $ics->setContentTypeParameter('METHOD', 'REPLY');

                    $mime->addPart($body);
                    $mime->addPart($ics);

                    // Build the reply headers
                    $msg_headers = &new MIME_Headers();
                    $msg_headers->addReceivedHeader();
                    $msg_headers->addMessageIdHeader();
                    $msg_headers->addHeader('Date', date('r'));
                    $msg_headers->addHeader('From', $email);
                    $msg_headers->addHeader('To', $organizerEmail);

                    $identity->setDefault(Util::getFormData('identity'));
                    $replyto = $identity->getValue('replyto_addr');
                    if (!empty($replyto) && ($replyto != $barefrom)) {
                        $msg_headers->addHeader('Reply-to', $replyto);
                    }
                    $msg_headers->addHeader('Subject', _("Free/Busy Request Response"));
                    $msg_headers->addMIMEHeaders($mime);

                    // Send the reply
                    $status = $mime->send($organizerEmail, $msg_headers);
                    if (is_a($status, 'PEAR_Error')) {
                        $this->_msgs[$key] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage()));
                    } else {
                        $this->_msgs[$key] = array('success', _("Reply Sent."));
                    }
                } else {
                    $this->_msgs[$key] = array('warning', _("Invalid Action selected for this component."));
                }
                break;

            case 'reply2m':
                // vFreebusy request.
                if (array_key_exists($key, $components) && $components[$key]->getType() == 'vFreebusy') {
                    $vFb = $components[$key];
                    $startStamp = time();
                    $endStamp = $startStamp + (60 * 24 * 3600);
                    $vfb_reply = $registry->call('calendar/getFreeBusy', array('startStamp' => $startStamp,
                                                                               'endStamp' => $endStamp));
                    require_once 'Horde/Identity.php';
                    require_once 'Horde/MIME.php';
                    require_once 'Horde/MIME/Headers.php';
                    require_once 'Horde/MIME/Part.php';
                    require_once 'Horde/Text.php';

                    // Find out who we are and update status.
                    $identity = &Identity::singleton();
                    $email = $identity->getValue('from_addr');
                    $cn = $identity->getValue('fullname');

                    // Get the organizer details
                    $organizer = parse_url($vFb->getAttribute('ORGANIZER'));
                    $organizerEmail = $organizer['path'];
                    $organizer = $vFb->getAttribute('ORGANIZER', true);
                    $organizerName = array_key_exists('cn', $organizer) ? $organizer['cn'] : '';

                    // Build the reply
                    $vCal = &new Horde_iCalendar();
                    $vCal->setAttribute('PRODID', '-//The Horde Project//' . HORDE_AGENT_HEADER . '//EN');
                    $vCal->setAttribute('METHOD', 'REPLY');
                    $vCal->addComponent($vfb_reply);

                    $mime = &new MIME_Message();
                    $message = _("Attached is an iCalendar file reply to a request you sent");
                    $body = &new MIME_Part('text/plain', Text::wrap($message, 76, "\n"));

                    $ics = &new MIME_Part('text/calendar', $vCal->exportvCalendar());
                    $ics->setName('icalendar.ics');
                    $ics->setContentTypeParameter('METHOD', 'REPLY');

                    $mime->addPart($body);
                    $mime->addPart($ics);

                    // Build the reply headers.
                    $msg_headers = &new MIME_Headers();
                    $msg_headers->addReceivedHeader();
                    $msg_headers->addMessageIdHeader();
                    $msg_headers->addHeader('Date', date('r'));
                    $msg_headers->addHeader('From', $email);
                    $msg_headers->addHeader('To', $organizerEmail);

                    $identity->setDefault(Util::getFormData('identity'));
                    $replyto = $identity->getValue('replyto_addr');
                    if (!empty($replyto) && ($replyto != $barefrom)) {
                        $msg_headers->addHeader('Reply-to', $replyto);
                    }
                    $msg_headers->addHeader('Subject', _("Free/Busy Request Response"));
                    $msg_headers->addMIMEHeaders($mime);

                    // Send the reply
                    $status = $mime->send($organizerEmail, $msg_headers);
                    if (is_a($status, 'PEAR_Error')) {
                        $this->_msgs[$key] = array('error', sprintf(_("Error sending reply: %s."), $status->getMessage()));
                    } else {
                        $this->_msgs[$key] = array('success', _("Reply Sent."));
                    }
                } else {
                    $this->_msgs[$key] = array('warning', _("Invalid Action selected for this component."));
                }
                break;

            case 'nosup':
                // vFreebusy request.
            default:
                $this->_msgs[$key] = array('warning', _("This action is not yet implemented."));
                break;
            }
        }

        // Create the HTML to display the iCal file.
        // Need to work out if we are inline and acutally need this
        $html = Util::bufferOutput('require', $registry->getParam('templates', 'horde') . '/common-header.inc');
        $html .= '<form method="post" name="iCal" action="' . Horde::selfURL(true) . '">';

        foreach ($components as $key => $component) {
            switch ($component->getType()) {
            case 'vEvent':
                $html .= $this->_vEvent($component, $key);
                break;

            case 'vTimeZone':
                // Ignore these.
                break;

            case 'vFreebusy':
                $html .= $this->_vFreebusy($component, $key);
                break;

            default:
                $html .= sprintf(_("Unhandled component of type: %s"), $component->getType());
            }
        }

        // Need to work out if we are inline and acutally need this
        $html .= "</form>";
        $html .= Util::bufferOutput('require', $registry->getParam('templates', 'horde') . '/common-footer.inc');

        return $html;
    }

    function _row($label, $value)
    {
        if (substr($label, 0, 2) == 'DT') {
            $value = strftime("%x %X", $value);
        }
        return '<tr><td valign="top" class="item">' . $label . '</td><td valign="top" class="item">' . $value . "</td></tr>\n";
    }

    /**
     * Return the html for a vFreebusy.
     */
    function _vFreebusy($vfb, $id)
    {
        global $registry, $conf;

        $html = '<table cellspacing="1" cellpadding="1" border="0">';

        $desc = '';
        $title = '';
        switch ($this->_method) {
        case 'PUBLISH':
            $desc = _("%s has sent you free/busy information.");
            $title = _("Free/Busy Information");
            break;

        case 'REQUEST':
            $desc = _("%s requests your free/busy information.");
            $title = _("Free/Busy Request");
            break;

        case 'REPLY':
            $desc = _("%s has replied to a free/busy request.");
            $title = _("Free/Busy Reply");
            break;
        }

        $desc = sprintf($desc, $vfb->getName());

        $html .= '<tr><td colspan="2" class="header">' . $title . '</td></tr>';
        $html .= '<tr><td colspan="2" class="control">' . $desc . '<br/>';
        $html .= _("Please select an action from the menu below.") . '</td></tr>';

        if (array_key_exists($id, $this->_msgs)) {
            $html .= '<tr><td colspan="2" class="smallheader"><b>' . Horde::img('alerts/' . $this->_msgs[$id][0] . '.gif', '', 'hspace="5"', $registry->getParam('graphics', 'horde')) . $this->_msgs[$id][1] . '</b></td></tr>';
        }

        $start = $vfb->getAttribute('DTSTART');
        if (!is_a($start, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("Start"), strftime($conf['mailbox']['date_format'] . ' ' . $conf['mailbox']['time_format'], $start));
        }

        $end = $vfb->getAttribute('DTEND');
        if (!is_a($end, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("End"), strftime("%x %X", $end));
        }

        $html .= '<tr><td colspan="2" class="control"><b>' . _("Actions") . ":</b><br/>";
        $html .= _("Choose an action:") . " <select name='action[$id]'>";

        switch ($this->_method) {
        case 'PUBLISH':
            if ($registry->hasMethod('calendar/import_vfreebusy')) {
                $html .= '<option value="import">' .   _("Remember the free/busy information.") . "</option>";
            } else {
                $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . "</option>";
            }
            break;

        case 'REQUEST':
            if ($registry->hasMethod('calendar/getFreeBusy')) {
                $html .= '<option value="reply">' .   _("Reply with requested free/busy information.") . "</option>";
                $html .= '<option value="reply2m">' . _("Reply with free/busy for next 2 months.") . "</option>";
            } else {
                $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . "</option>";
            }

            $html .= '<option value="deny">' . _("Deny request for free/busy information") . "</option>";
            break;

        case 'REPLY':
            if ($registry->hasMethod('calendar/import_vfreebusy')) {
                $html .= '<option value="import">' .   _("Remember the free/busy information.") . "</option>";
            } else {
                $html .= '<option value="nosup">' . _("Reply with Not Supported Message") . "</option>";
            }
            break;
        }

        $html .= sprintf('</select>&nbsp<input type="submit" class="button" value="%s" /><br />', _("OK"));
        $html .= "</td></tr>";
        $html .= '</table>';
        return $html;
    }

    /**
     * Return the html for a vEvent
     */
    function _vEvent($vevent, $id)
    {
        global $registry, $conf;

        $html = '<table cellspacing="1" cellpadding="1" border="0">';

        $desc = '';
        $title = '';
        switch ($this->_method) {
        case 'PUBLISH':
            $desc = _("%s wishes to make you aware of %s.");
            $title = _("Meeting Information");
            break;

        case 'REQUEST':
            // Check that you are one of the attendess here
            $desc = _("%s requests your presence at %s.");
            $title = _("Meeting Proposal");
            break;

        case 'ADD':
            $desc = _("%s wishes to add to %s.");
            $title = _("Meeting Update");
            break;

        case 'REFRESH':
            $desc = _("%s wishes to receive the latest information about %s.");
            $title = _("Meeting Update Request");
            // Fetch the event info from the UID here
            break;

        case 'REPLY':
            $desc = _("%s has replied to the invitation to %s.");
            $title = _("Meeting Reply");
            // Fetch the event info from the UID here
            break;

        case 'CANCEL':
            $desc = _("%s has cancelled %s.");
            $title = _("Meeting Cancellation");
            // Fetch the event info from the UID here
            break;
        }

        $desc = sprintf($desc, $vevent->organizerName(), $vevent->getAttribute('SUMMARY'));

        $html .= '<tr><td colspan="2" class="header">' . $title . '</td></tr>';
        $html .= '<tr><td colspan="2" class="control">' . $desc . '<br/>';
        $html .= _("Please review the following information, and then select an action from the menu below.") . '</td></tr>';

        if (array_key_exists($id, $this->_msgs)) {
            $html .= '<tr><td colspan="2" class="smallheader"><b>' . Horde::img('alerts/' . $this->_msgs[$id][0] . '.gif', '', 'hspace="5"', $registry->getParam('graphics', 'horde')) . $this->_msgs[$id][1] . '</b></td></tr>';
        }

        $start = $vevent->getAttribute('DTSTART');
        if (!is_a($start, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("Start"), strftime($conf['mailbox']['date_format'] . ' ' . $conf['mailbox']['time_format'], $start));
        }

        $end = $vevent->getAttribute('DTEND');
        if (!is_a($end, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("End"), strftime("%x %X", $end));
        }

        $sum = $vevent->getAttribute('SUMMARY');
        if (!is_a($sum, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("Summary"), $sum);
        } else {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> <i>%s</i></td></tr>', _("Summary"), _("None"));
        }

        $desc = $vevent->getAttribute('DESCRIPTION');
        if (!is_a($desc, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("Description"), $desc);
        }

        $loc = $vevent->getAttribute('LOCATION');
        if (!is_a($loc, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b> %s</td></tr>', _("Location"), $loc);
        }

        $attendees = $vevent->getAttribute('ATTENDEE');
        $params    = $vevent->getAttribute('ATTENDEE', true);

        if (!is_a($attendees, 'PEAR_Error')) {
            $html .= sprintf('<tr><td colspan="2" class="item"><b>%s:</b><br/>', _("Attendees"));
            if (!is_array($attendees)) {
                $attendees = array($attendees);
                $params    = array($params);
            }

            $html .= sprintf('<table width="100%%"><tr><th align="left">%s</th><th align="left">%s</th><th align="left">%s</th></tr>', _("Name"), _("Role"), _("Status"));
            foreach ($attendees as $key => $attendee) {
                $attendee = parse_url($attendee);
                $attendee = $attendee['path'];

                if (array_key_exists('CN', $params[$key])) {
                    $attendee = $params[$key]['CN'];
                }

                $role = _("Required Participant");
                if (array_key_exists('ROLE', $params[$key])) {
                    switch ($params[$key]['ROLE']) {
                    case 'CHAIR':
                        $role = _("Chair Person");
                        break;

                    case 'OPT-PARTICIPANT':
                        $role = _("Optional Participant");
                        break;

                    case 'NON-PARTICIPANT':
                        $role = _("Non Participant");
                        break;

                    case 'REQ-PARTICIPANT':
                    default:
                        // Already set above.
                        break;
                    }
                }

                $status = _("Awaiting Response");
                if (array_key_exists('PARTSTAT', $params[$key])) {
                    $status = $this->_partstatToString($params[$key]['PARTSTAT'], $status);
                }

                $html .= sprintf('<tr><td>%s</td><td>%s</td><td>%s</td></tr>', $attendee, $role, $status);
            }
            $html .= "</table>";
            $html .= '</td></tr>';
        }

        $html .= '<tr><td colspan="2" class="control"><b>' . _("Actions") . ":</b><br/>";
        $html .= _("Choose an action:") . " <select name='action[$id]'>";

        switch ($this->_method) {
        case 'PUBLISH':
            if ($registry->hasMethod('calendar/import')) {
                $html .= '<option value="import">' .   _("Add this to my calendar") . "</option>";
            }
            break;

        case 'REQUEST':
            if ($registry->hasMethod('calendar/import')) {
                $html .= '<option value="import">' .   _("Add this to my calendar") . "</option>";
            }

            $html .= '<option value="accept">' . _("Accept request") . "</option>";
            $html .= '<option value="tentative">' . _("Tentatively Accept request") . "</option>";
            $html .= '<option value="deny">' . _("Deny request") . "</option>";
            $html .= '<option value="delegate">' . _("Delegate position") . "</option>";
            break;

        case 'REPLY':
            if ($registry->hasMethod('calendar/update_meeting')) {
                $html .= '<option value="import">' . _("Update respondent status") . "</option>";
            }
            break;

        case 'REFRESH':
            $html .= '<option value="send">' . _("Send Latest Information") . "</option>";
            break;

        case 'CANCEL':
            if ($registry->hasMethod('calendar/delete_event')) {
                $html .= '<option value="import">' . _("Remove from my calendar") . "</option>";
            }
            break;
        }

        $html .= sprintf('</select>&nbsp<input type="submit" class="button" value="%s" /><br />', _("OK"));
        $html .= '</td></tr>';
        $html .= '</table>';
        return $html;
    }

    /**
     * Translate the Participation status to string
     *
     * @param string    $value  The value of PARTSTAT
     * @param string    $default The value to return as default.
     *
     * @return string   The translated string.
     */
    function _partstatToString($value, $default = null)
    {
        switch ($value) {
        case 'ACCEPTED':
            return _("Accepted");
            break;

        case 'DECLINED':
            return _("Declined");
            break;

        case 'TENTATICE':
            return _("Tentatively Accepted");
            break;

        case 'DELEGATED':
            return _("Delegated");
            break;

        case 'COMPLETED':
            return _("Completed");
            break;

        case 'IN-PROCESS':
            return _("In Process");
            break;

        case 'NEEDS-ACTION':
        default:
            return is_null($default) ? _("Needs Action") : $default;
        }
    }

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

}

--- NEW FILE: images.php ---
<?php
/**
 * The MIME_Viewer_images class allows images to be displayed.
 *
 * $Horde: framework/MIME/MIME/Viewer/images.php,v 1.15 2004/04/26 18:21:22 chuck Exp $
 *
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 2.2
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_images extends MIME_Viewer {

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

        if ($browser->isViewable($this->getType())) {
            return $this->mime_part->getContents();
        }
    }

    /**
     * Return the content-type.
     *
     * @access public
     *
     * @return string  The content-type of the output.
     */
    function getType()
    {
        global $browser;

        $type = $this->mime_part->getType();
        if ($browser->isBrowser('mozilla') && ($type == 'image/pjpeg')) {
            /* image/jpeg and image/pjpeg *appear* to be the same
             * entity, but Mozilla don't seem to want to accept the
             * latter.  For our purposes, we will treat them the
             * same. */
            return 'image/jpeg';
        } elseif ($type == 'image/x-png') {
            /* image/x-png is equivalent to image/png. */
            return 'image/png';
        } else {
            return $type;
        }
    }

    /**
     * Generate HTML output for a javascript auto-resize view window.
     *
     * @access private
     *
     * @param string $url    The URL which contains the actual image data.
     * @param string $title  The title to use for the page.
     *
     * @return string  The HTML output.
     */
    function _popupImageWindow($url, $title)
    {
        $str = <<<EOD
<html>
<head>
<title>$title</title>
<script language="javascript" type="text/javascript">
function resizeWindow()
{
    window.moveTo(0, 0);
    window.resizeTo(200, 200);

    width_1 = document.disp_image.width;
    width_2 = window.screen.availWidth - 20;
    width = (width_1 > width_2) ? width_2 : width_1;

    height_1 = document.disp_image.height;
    height_2 = window.screen.availTop || (window.screen.height - 20);
    height = (height_1 > height_2) ? height_2 : height_1;

    window.resizeTo(width + (200 - (document.body.clientWidth || window.innerWidth)), height + (200 - (document.body.clientHeight || window.innerHeight)));
    window.focus();
}
</script>
</head>
<body bgcolor="#ffffff" onload="javascript:resizeWindow();" topmargin="0" marginheight="0" leftmargin="0" marginwidth="0">
<img name="disp_image" border="0" src="$url" style="display:block" />
</body>
</html>
EOD;
        return $str;
    }

}

--- NEW FILE: msexcel.php ---
<?php
/**
 * The MIME_Viewer_msexcel class renders out Microsoft Excel
 * documents in HTML format by using the xlHtml package.
 *
 * $Horde: framework/MIME/MIME/Viewer/msexcel.php,v 1.20 2004/04/16 17:21:45 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_msexcel extends MIME_Viewer {

    /**
     * Render out the currently data using xlhtml.
     *
     * @access public
     *
     * @param optional array $params  Any params this Viewer may need.
     *
     * @return string  The rendered data.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['msexcel']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['msexcel']['location']) . '</pre>';
        }

        $data = '';
        $tmp_xls = Horde::getTempFile('horde_msexcel');

        $fh = fopen($tmp_xls, 'w');
        fwrite($fh, $this->mime_part->getContents());
        fclose($fh);

        $fh = popen($mime_drivers['horde']['msexcel']['location'] . " -nh $tmp_xls 2>&1", 'r');
        while (($rc = fgets($fh, 8192))) {
            $data .= $rc;
        }
        pclose($fh);

        return $data;
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: mspowerpoint.php ---
<?php
/**
 * The MIME_Viewer_mspowerpoint class renders out Microsoft Powerpoint
 * documents in HTML format by using the xlHtml package.
 *
 * $Horde: framework/MIME/MIME/Viewer/mspowerpoint.php,v 1.13 2004/04/16 17:21:45 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_mspowerpoint extends MIME_Viewer {

    /**
     * Render out the current data using ppthtml.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['mspowerpoint']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['mspowerpoint']['location']) . '</pre>';
        }

        $data = '';
        $tmp_ppt = Horde::getTempFile('horde_mspowerpoint');

        $fh = fopen($tmp_ppt, 'w');
        fwrite($fh, $this->mime_part->getContents());
        fclose($fh);

        $fh = popen($mime_drivers['horde']['mspowerpoint']['location'] . " $tmp_ppt 2>&1", 'r');
        while (($rc = fgets($fh, 8192))) {
            $data .= $rc;
        }
        pclose($fh);

        return $data;
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: msword.php ---
<?php
/**
 * The MIME_Viewer_msword class renders out Microsoft Word
 * documents in HTML format by using the wvWare package.
 *
 * $Horde: framework/MIME/MIME/Viewer/msword.php,v 1.25 2004/05/04 22:07:18 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_msword extends MIME_Viewer {

    /**
     * Render out the current data using wvWare.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['msword']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['msword']['location']) . '</pre>';
        }

        $data = '';
        $tmp_word   = Horde::getTempFile('msword');
        $tmp_output = Horde::getTempFile('msword');
        $tmp_dir    = Horde::getTempDir();
        $tmp_file   = str_replace($tmp_dir . '/', '', $tmp_output);

        if (OS_WINDOWS) {
            $args = ' -x ' . dirname($mime_drivers['horde']['msword']['location']) . "\\wvHtml.xml -d $tmp_dir -1 $tmp_word > $tmp_output";
        } else {
            $version = exec($mime_drivers['horde']['msword']['location'] . ' --version');
            if (version_compare($version, '0.7.0') >= 0) {
                $args = " --targetdir=$tmp_dir $tmp_word $tmp_file";
            } else {
                $args = " $tmp_word $tmp_output";
            }
        }

        $fh = fopen($tmp_word, 'w');
        fwrite($fh, $this->mime_part->getContents());
        fclose($fh);

        exec($mime_drivers['horde']['msword']['location'] . $args);

        if (!file_exists($tmp_output)) {
            return _("Unable to translate this Word document");
        }

        $fh = fopen($tmp_output, 'r');
        while (($rc = fgets($fh, 8192))) {
            $data .= $rc;
        }
        fclose($fh);
        
        return $data;
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: ooo.php ---
<?php
/**
 * The MIME_Viewer_ooo class renders out OpenOffice.org
 * documents in HTML format.
 *
 * $Horde: framework/MIME/MIME/Viewer/ooo.php,v 1.13 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 2003-2004 Marko Djukic <marko at oblo.com>
 * Copyright 2003-2004 Jan Schneider <jan at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Marko Djukic <marko at oblo.com>
 * @author  Jan Schneider <jan at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_ooo extends MIME_Viewer {

    /**
     * Render out the current data.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        $use_xslt = Util::extensionExists('xslt') || function_exists('domxml_xslt_stylesheet_file');
        if ($use_xslt) {
            $tmpdir = Util::createTempDir(true);
        }

        require_once 'Horde/Compress.php';
        $xml_tags  = array('text:p', 'table:table ', 'table:table-row', 'table:table-cell', 'table:number-columns-spanned=');
        $html_tags = array('p', 'table border="0" cellspacing="1" cellpadding="0" ', 'tr bgcolor="#cccccc"', 'td', 'colspan=');
        $zip = &Horde_Compress::singleton('zip');
        $list = $zip->decompress($this->mime_part->getContents(),
            array('action' => HORDE_COMPRESS_ZIP_LIST));
        foreach ($list as $key => $file) {
            if ($file['name'] == 'content.xml' ||
                $file['name'] == 'styles.xml' ||
                $file['name'] == 'meta.xml') {
                $content = $zip->decompress($this->mime_part->getContents(),
                    array('action' => HORDE_COMPRESS_ZIP_DATA,
                          'info'   => $list,
                          'key'    => $key));
                if ($use_xslt) {
                    $fp = fopen($tmpdir . $file['name'], 'w');
                    fwrite($fp, $content);
                    fclose($fp);
                } elseif ($file['name'] == 'content.xml') {
                    $content = str_replace($xml_tags, $html_tags, $content);
                    return $content;
                }
            }
        }
        if (!Util::extensionExists('xslt')) {
            return;
        }

        if (function_exists('domxml_xslt_stylesheet_file')) {
            // Use DOMXML
            $xslt = domxml_xslt_stylesheet_file('Horde/MIME/Viewer/ooo/main_html.xsl');
            $dom  = domxml_open_file($tmpdir . 'content.xml');
            $result = $xslt->process($dom, array('metaFileURL' => $tmpdir . 'meta.xml', 'stylesFileURL' => $tmpdir . 'styles.xml', 'disableJava' => true));
            return $xslt->result_dump_mem($result);
        } else {
            // Use XSLT
            $xslt = xslt_create();
            $result = @xslt_process($xslt, $tmpdir . 'content.xml',
                                    'Horde/MIME/Viewer/ooo/main_html.xsl', null, null,
                                    array('metaFileURL' => $tmpdir . 'meta.xml', 'stylesFileURL' => $tmpdir . 'styles.xml', 'disableJava' => true));
            if (!$result) {
                $result = xslt_error($xslt);
            }
            xslt_free($xslt);
            return $result;
        }
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output.
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: pdf.php ---
<?php
/**
 * The MIME_Viewer_pdf class simply outputs the PDF file with the content-type
 * 'application/pdf' enabling web browsers with a PDF viewer plugin to view
 * the PDF file inside the browser.
 *
 * $Horde: framework/MIME/MIME/Viewer/pdf.php,v 1.3 2004/01/01 15:14:21 jan Exp $
 *
 * Copyright 2003-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_pdf extends MIME_Viewer {

    /**
     * Return the content-type.
     *
     * @access public
     *
     * @return string  The content-type of the output.
     */
    function getType()
    {
        return 'application/pdf';
    }

}

--- NEW FILE: php.php ---
<?php

require_once dirname(__FILE__) . '/source.php';

/**
 * The MIME_Viewer_php class renders out syntax-highlighted PHP code
 * in HTML format.
 *
 * $Horde: framework/MIME/MIME/Viewer/php.php,v 1.21 2004/05/22 03:06:23 chuck Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_php extends MIME_Viewer_source {

    /**
     * Renders out the contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        return $this->lineNumber(trim(str_replace(array("\n", '<br />'), array('', "\n"),
                                                  highlight_string($this->mime_part->getContents(), true))));
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output.
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: plain.php ---
<?php
/**
 * The MIME_Viewer_text class renders out plain text with URLs made
 * into hyperlinks (if viewing inline).
 *
 * $Horde: framework/MIME/MIME/Viewer/plain.php,v 1.15 2004/04/14 12:38:38 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 * Copyright 2002-2004 Michael Slusarz <slusarz at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @author  Michael Slusarz <slusarz at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_plain extends MIME_Viewer {

    /**
     * Render out the contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        require_once 'Horde/MIME/Contents.php';

        $text = $this->mime_part->getContents();

        /* Check for 'flowed' text data. */
        $flowed = ($this->mime_part->getContentTypeParameter('format') == 'flowed');
        if ($flowed) {
            $text = $this->_formatFlowed($text);
        }

        /* If calling as an attachment from view.php, we do not want
           to alter the text in any way with HTML. */
        if (MIME_Contents::viewAsAttachment()) {
            return $text;
        } else {
            require_once 'Horde/Text.php';
            return Text::toHTML($text, TEXT_HTML_MICRO, null, null);
        }
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        require_once 'Horde/MIME/Contents.php';
        return (MIME_Contents::viewAsAttachment()) ? $this->mime_part->getType(true) : 'text/html';
    }

    /**
     * Format flowed text for HTML output.
     *
     * @access public
     *
     * @param string $text  The text to format.
     *
     * @return string  The formatted text.
     */
    function _formatFlowed($text)
    {
        require_once 'Horde/Text/Flowed.php';
        $flowed = &new Text_Flowed($this->mime_part->replaceEOL($text, "\n"));
        $flowed->setOptLength(90);
        return $flowed->toFixed();
    }

}

--- NEW FILE: rar.php ---
<?php
/**
 * The MIME_Viewer_rar class renders out the contents of .rar archives in HTML
 * format.
 *
 * $Horde: framework/MIME/MIME/Viewer/rar.php,v 1.18 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 * Copyright 2002-2004 Michael Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @author  Michael Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_rar extends MIME_Viewer {

    /**
     * Rar compression methods.
     *
     * @var array $_methods
     */
    var $_methods = array(
        0x30  =>  'Store',
        0x31  =>  'Fastest',
        0x32  =>  'Fast',
        0x33  =>  'Normal',
        0x34  =>  'Good',
        0x35  =>  'Best'
    );

    /**
     * Render out the currently set contents using rar.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        $contents = $this->mime_part->getContents();

        /* Make sure this is a valid rar file. */
        if ($this->checkRarData($contents) === false) {
            return '<pre>' . _("This does not appear to be a valid rar archive.") . '</pre>';
        }

        require_once 'Horde/Text.php';

        $fileCount = count($rarData);
        $rarData = $this->getRarData($contents);

        $text  = '<b>' . htmlspecialchars(sprintf(_("Contents of '%s'"), $this->mime_part->getName())) . ':</b>' . "\n";
        $text .= '<table><tr><td align="left"><tt><span class="fixed">';
        $text .= Text::htmlAllSpaces(_("Archive Name") . ':  ' . $this->mime_part->getName()) . "\n";
        $text .= Text::htmlAllSpaces(_("Archive File Size") . ': ' . strlen($contents) . ' bytes') . "\n";
        $text .= Text::htmlAllSpaces(($fileCount != 1) ? sprintf(_("File Count: %s files"), $fileCount) : sprintf(_("File Count: %s file"), $fileCount));
        $text .= "\n\n";
        $text .= Text::htmlAllSpaces(
                     str_pad(_("File Name"),     50, ' ', STR_PAD_RIGHT) .
                     str_pad(_("Attributes"),    10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Size"),          10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Modified Date"), 19, ' ', STR_PAD_LEFT) .
                     str_pad(_("Method"),        10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Ratio"),          7, ' ', STR_PAD_LEFT)
                 ) . "\n";

        $text .= str_repeat('-', 106) . "\n";

        foreach ($rarData as $val) {
            $ratio = (empty($val['size'])) ? 0 : 100 * ($val['csize'] / $val['size']);
            $text .= Text::htmlAllSpaces(
                         str_pad($val['name'], 50, ' ', STR_PAD_RIGHT) .
                         str_pad($val['attr'], 10, ' ', STR_PAD_LEFT) .
                         str_pad($val['size'], 10, ' ', STR_PAD_LEFT) .
                         str_pad(strftime("%d-%b-%Y %H:%M", $val['date']), 19, ' ', STR_PAD_LEFT) .
                         str_pad($val['method'], 10, ' ', STR_PAD_LEFT) .
                         str_pad(sprintf("%1.1f%%", $ratio), 7, ' ', STR_PAD_LEFT)
                     ) . "\n";
        }

        $text .= str_repeat('-', 106) . "\n";
        $text .= '</span></tt></td></tr></table>';

        return nl2br($text);
    }

    /**
     * Returns the MIME type of this part.
     *
     * @access public
     *
     * @return string  The MIME type of this part.
     */
    function getType()
    {
        return 'text/html';
    }

    /**
     * Checks to see if the data is a valid Rar archive.
     *
     * @access public
     *
     * @param string &$data  The rar archive data.
     *
     * @return boolean  True if valid, false if invalid.
     */
    function checkRarData(&$data)
    {
        $fileHeader = "\x52\x61\x72\x21\x1a\x07\x00";
        if (strpos($data, $fileHeader) === false) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Get the list of files/data from the rar archive.
     *
     * @access public
     *
     * @param string &$data  The rar archive data.
     *
     * @return array  KEY: Position in RAR archive
     *                VALUES: 'attr'    --  File attributes
     *                        'date'    --  File modification time
     *                        'csize'   --  Compressed file size
     *                        'method'  --  Compression method
     *                        'name'    --  Filename
     *                        'size'    --  Original file size
     */
    function getRarData(&$data)
    {
        $return_array = array();

        $blockStart = strpos($data, "\x52\x61\x72\x21\x1a\x07\x00");
        $position = $blockStart + 7;

        while ($position < strlen($data)) {
            $head_crc   = substr($data, $position + 0, 2);
            $head_type  = ord(substr($data, $position + 2, 1));
            $head_flags = unpack('vFlags', substr($data, $position + 3, 2));
            $head_flags = $head_flags['Flags'];
            $head_size  = unpack('vSize', substr($data, $position + 5, 2));
            $head_size  = $head_size['Size'];

            $position += 7;
            $head_size -= 7;

            switch ($head_type) {

            case 0x73:
                /* Archive header */
                $position += $head_size;

                break;

            case 0x74:
                $file = array();

                /* File Header */
                $info = unpack('VPacked/VUnpacked/COS/VCRC32/VTime/CVersion/CMethod/vLength/vAttrib', substr($data, $position));

                $file['name'] = substr($data, $position + 25, $info['Length']);
                $file['size'] = $info['Unpacked'];
                $file['csize'] = $info['Packed'];

                $file['date'] = mktime((($info['Time'] >> 11) & 0x1f),
                                       (($info['Time'] >> 5) & 0x3f),
                                       (($info['Time'] << 1) & 0x3e),
                                       (($info['Time'] >> 21) & 0x07), 
                                       (($info['Time'] >> 16) & 0x1f), 
                                       ((($info['Time'] >> 25) & 0x7f) + 80));

                $file['method'] = $this->_methods[$info['Method']];

                $file['attr']  = '';
                $file['attr'] .= ($info['Attrib'] & 0x10) ? 'D' : '-';
                $file['attr'] .= ($info['Attrib'] & 0x20) ? 'A' : '-';
                $file['attr'] .= ($info['Attrib'] & 0x03) ? 'S' : '-';
                $file['attr'] .= ($info['Attrib'] & 0x02) ? 'H' : '-';
                $file['attr'] .= ($info['Attrib'] & 0x01) ? 'R' : '-';

                $return_array[] = $file;

                $position += $head_size;
                $position += $info['Packed'];
                break;

            default:
                $position += $head_size;
                if (isset($add_size)) { 
                    $position += $add_size;
                }
                break;

            }
        }
        
        return $return_array;
    }

}

--- NEW FILE: report.php ---
<?php
/**
 * The MIME_Viewer_report class is a wrapper used to load the appropriate
 * MIME_Viewer for multipart/report data (RFC 3462).
 *
 * $Horde: framework/MIME/MIME/Viewer/report.php,v 1.5 2004/01/01 15:14:21 jan Exp $
 *
 * Copyright 2003-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_report extends MIME_Viewer {

    /**
     * Stores the MIME_Viewer of the specified protocol.
     *
     * @var object MIME_Viewer $_viewer
     */
    var $_viewer;

    /**
     * Render the multipart/report data. 
     *
     * @access public
     *
     * @param optional array $params  An array of parameters needed.
     *
     * @return string  The rendered data.
     */
    function render($params = array())
    {
        /* Get the appropriate MIME_Viewer for the protocol specified. */
        if (!($this->_resolveViewer())) {
            return;
        }

        /* Render using the loaded MIME_Viewer object. */
        return $this->_viewer->render($params);
    }

    /**
     * Returns the content-type of the Viewer used to view the part.
     *
     * @access public
     *
     * @return string  A content-type string.
     */
    function getType()
    {
        /* Get the appropriate MIME_Viewer for the protocol specified. */
        if (!($this->_resolveViewer())) {
            return 'application/octet-stream';
        } else {
            return $this->_viewer->getType();
        }
    }

    /**
     * Load a MIME_Viewer according to the report-type parameter stored
     * in the MIME_Part to render. If unsuccessful, try to load a generic
     * multipart MIME_Viewer.
     *
     * @access private
     *
     * @return boolean  True on success, false on failure.
     */
    function _resolveViewer()
    {
        $viewer = null;

        if (empty($this->_viewer)) {
            if (!($type = $this->mime_part->getContentTypeParameter('report-type'))) {
                return false;
            }

            $viewer = &MIME_Viewer::factory($this->mime_part, 'message/' . String::lower($type));
            if (empty($viewer) ||
                (get_class($viewer) == 'mime_viewer_default')) {
                if (!($viewer = &MIME_Viewer::factory($this->mime_part, $this->mime_part->getPrimaryType() . '/*'))) {
                    return false;
                }
            }
            $this->_viewer = $viewer;
        }

        return true;
    }

}

--- NEW FILE: rfc822.php ---
<?php
/**
 * The MIME_Viewer_rfc822 class renders out messages from the
 * message/rfc822 content type.
 *
 * $Horde: framework/MIME/MIME/Viewer/rfc822.php,v 1.3 2004/04/17 14:19:53 jan Exp $
 *
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.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.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_Mime_Viewer
 */
class MIME_Viewer_rfc822 extends MIME_Viewer {

    /**
     * Render out the currently set contents.
     *
     * @access public
     *
     * @param optional array $params  An array with any parameters needed.
     *
     * @return string  The rendered text.
     */
    function render($params = array())
    {
        $part = $this->mime_part;
        $part->transferDecodeContents();

        $text = $part->getContents();
        if (!$text) {
            require_once 'Horde/MIME/Contents.php';
            $contents = &new MIME_Contents(new MIME_Part());
            return $contents->formatStatusMsg(_("There was an error displaying this message part"));
        } else {
            return $text;
        }
    }

    /**
     * Render out attachment information.
     *
     * @access public
     *
     * @param optional array $params  An array with any parameters needed.
     *
     * @return string  The rendered text in HTML.
     */
    function renderAttachmentInfo($params = array())
    {
        $msg = '';

        /* Get the text of the part.  Since we need to look for the end of
         * the headers by searching for the CRLFCRLF sequence, use
         * getCanonicalContents() to make sure we are getting the text with
         * CRLF's. */
        $text = $this->mime_part->getCanonicalContents();

        /* Search for the end of the header text (CRLFCRLF). */
        $text = substr($text, 0, strpos($text, "\r\n\r\n"));

        /* Get the list of headers now. */
        require_once 'Horde/MIME/Structure.php';
        $headers = &MIME_Structure::parseMIMEHeaders($text, true, true);

        require_once 'Horde/MIME/Contents.php';
        $contents = &new MIME_Contents(new MIME_Part());
        $msg = $contents->formatStatusMsg(_("The following are the headers for this message/rfc822 message."), '<img src="' . $GLOBALS['registry']->getParam('webroot') . '/graphics/info_icon.gif" height="16" width="16" border="0" alt="' . _("Info") . '" />', false);

        $msg .= '<span class="fixed">';

        $header_array = array(
            'date' => _("Date"),
            'subject' => _("Subject"),
            'from' => _("From"),
            'to' => _("To")
        );
        $header_output = array();
 
        foreach ($header_array as $key => $val) {
            if (isset($headers[$key])) {
                $header_output[] = '<b>' . $val . ':</b> ' . htmlspecialchars($headers[$key]);
            }
        }

        $msg .= implode("<br />\n", $header_output) . '</span>';

        return $msg;
    }

    /**
     * Return the MIME content type for the rendered data.
     *
     * @access public
     *
     * @return string  The content type of the data.
     */
    function getType()
    {
        return 'text/plain';
    }

}

--- NEW FILE: richtext.php ---
<?php
/**
 * The MIME_Viewer_richtext class renders out HTML text from text/richtext
 * content tags, (RFC 1896 [7.1.3]).
 *
 * A minimal richtext implementation is one that simply converts "<lt>" to
 * "<", converts CRLFs to SPACE, converts <nl> to a newline according to
 * local newline convention, removes everything between a <comment> command
 * and the next balancing </comment> command, and removes all other
 * other formatting commands (all text enclosed in angle brackets).
 *
 * We implement the following tags:
 * <bold>, <italic>, <fixed>, <smaller>, <bigger>, <underline>, <center>,
 * <flushleft>, <flushright>, <indent>, <subscript>, <excerpt>, <paragraph>,
 * <signature>, <comment>, <no-op>, <lt>, <nl>
 *
 * The following tags are implemented differently than described in the RFC
 * (due to limitations in HTML output):
 * <heading> - Output as centered, bold text.
 * <footing> - Output as centered, bold text.
 * <np>      - Output as paragraph break.
 *
 * The following tags are NOT implemented:
 * <indentright>, <outdent>, <outdentright>, <samepage>, <iso-8859-X>,
 * <us-ascii>, 
 *
 * $Horde: framework/MIME/MIME/Viewer/richtext.php,v 1.4 2004/04/13 18:03:51 slusarz Exp $
 *
 * Copyright 2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_richtext extends MIME_Viewer {

    /**
     * Render out the currently set contents in HTML format.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        if (($text = $this->mime_part->getContents()) === false) {
            return false;
        }

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

        /* Use str_ireplace() if using PHP 5.0+. */
        $has_str_ireplace = function_exists('str_ireplace');

        /* 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 . ' ';

        /* Remove everything between <comment> tags. */
        $text = preg_replace('/<comment.*>.*<\/comment>/Uis', '', $text);

        /* Remove any unrecognized tags in the text.  We don't need <no-op>
         * in $tags since it doesn't do anything anyway.  All <comment> tags
         * have already been removed. */
        $tags = '<bold><italic><fixed><smaller><bigger><underline><center><flushleft><flushright><indent><subscript><excerpt><paragraph><signature><lt><nl>';
        $text = strip_tags($text, $tags);

        /* <lt> becomes a '<'. CRLF becomes a SPACE. */
        if ($has_str_ireplace) {
            $text = str_ireplace(array('<lt>', "\r\n"), array('<', ' '));
        } else {
            $text = preg_replace(array('/<lt>/i', "/\r\n/"), array('<', ' '), $text);
        }

        /* We try to protect against bad stuff here. */
        $text = @htmlspecialchars($text, ENT_QUOTES, $this->mime_part->getCharset());

        /* <nl> becomes a newline (<br />);
         * <np> becomes a paragraph break (<p />). */
        if ($has_str_ireplace) {
            $text = str_ireplace(array('<nl>', '<np>'), array('<br />', '<p />'));
        } else {
            $text = preg_replace(array('/(?<!<)<nl.*>/Uis', '/(?<!<)<np.*>/Uis'), array('<br />', '<p />'), $text);
        }

        /* 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('/(?<!<)<bold.*>(.*)<\/bold>/Uis', '<span style="font-weight: bold">\1</span>', $text);
        $text = preg_replace('/(?<!<)<italic.*>(.*)<\/italic>/Uis', '<span style="font-style: italic">\1</span>', $text);
        $text = preg_replace('/(?<!<)<fixed.*>(.*)<\/fixed>/Uis', '<font face="fixed">\1</font>', $text);
        $text = preg_replace('/(?<!<)<smaller.*>/Uis', '<span style="font-size: smaller">', $text);
        $text = preg_replace('/(?<!<)<\/smaller>/Uis', '</span>', $text);
        $text = preg_replace('/(?<!<)<bigger.*>/Uis', '<span style="font-size: larger">', $text);
        $text = preg_replace('/(?<!<)<\/bigger>/Uis', '</span>', $text);
        $text = preg_replace('/(?<!<)<underline.*>(.*)<\/underline>/Uis', '<span style="text-decoration: underline">\1</span>', $text);
        $text = preg_replace('/(?<!<)<center.*>(.*)<\/center>/Uis', '<div align="center">\1</div>', $text);
        $text = preg_replace('/(?<!<)<flushleft.*>(.*)<\/flushleft>/Uis', '<div align="left">\1</div>', $text);
        $text = preg_replace('/(?<!<)<flushright.*>(.*)<\/flushright>/Uis', '<div align="right">\1</div>', $text);
        $text = preg_replace('/(?<!<)<indent.*>(.*)<\/indent>/Uis', '<blockquote>\1</blockquote>', $text);
        $text = preg_replace('/(?<!<)<excerpt.*>(.*)<\/excerpt>/Uis', '<cite>\1</cite>', $text);
        $text = preg_replace('/(?<!<)<subscript.*>(.*)<\/subscript>/Uis', '<sub>\1</sub>', $text);
        $text = preg_replace('/(?<!<)<superscript.*>(.*)<\/superscript>/Uis', '<sup>\1</sup>', $text);
        $text = preg_replace('/(?<!<)<heading.*>(.*)<\/heading>/Uis', '<br /><div align="center" style="font-weight: bold">\1</div><br />', $text);
        $text = preg_replace('/(?<!<)<footing.*>(.*)<\/footing>/Uis', '<br /><div align="center" style="font-weight: bold">\1</div><br />', $text);
        $text = preg_replace('/(?<!<)<paragraph.*>(.*)<\/paragraph>/Uis', '<p>\1</p>', $text);
        $text = preg_replace('/(?<!<)<signature.*>(.*)<\/signature>/Uis', '<address>\1</address>', $text);

        /* Now we remove the leading/trailing space we added at the start. */
        $text = substr($text, 1, -1);

        /* Wordwrap. */
        $text = str_replace("\t", '        ', $text);
        $text = str_replace('  ', '  ', $text);
        $text = str_replace("\n ", "\n ", $text);
        if ($text[0] == ' ') {
            $text = ' ' . substr($text, 1);
        }
        $text = nl2br($text);
        $text = '<p class="fixed">' . $text . '</p>';

        return $text;
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: rpm.php ---
<?php
/**
 * The MIME_Viewer_rpm class renders out lists of files in RPM
 * packages by using the rpm tool to query the package.
 *
 * $Horde: framework/MIME/MIME/Viewer/rpm.php,v 1.12 2004/04/16 17:21:45 jan Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_rpm extends MIME_Viewer {

    /**
     * Render out the RPM contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['rpm']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['rpm']['location']) . '</pre>';
        }

        $data = '';
        $tmp_rpm = Horde::getTempFile('horde_rpm');

        $fh = fopen($tmp_rpm, 'w');
        fwrite($fh, $this->mime_part->getContents());
        fclose($fh);

        $fh = popen($mime_drivers['horde']['rpm']['location'] . " -qip $tmp_rpm 2>&1", 'r');
        while (($rc = fgets($fh, 8192))) {
            $data .= $rc;
        }
        pclose($fh);

        return '<pre>' . htmlentities($data) . '</pre>';
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: security.php ---
<?php
/**
 * The MIME_Viewer_security class is a wrapper used to load the appropriate
 * MIME_Viewer for secure multipart messages (defined by RFC 1847). This
 * class handles multipart/signed and multipart/encrypted data.
 *
 * $Horde: framework/MIME/MIME/Viewer/security.php,v 1.5 2004/01/01 15:14:21 jan Exp $
 *
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_security extends MIME_Viewer {

    /**
     * Stores the MIME_Viewer of the specified security protocol.
     *
     * @var object MIME_Viewer $_viewer
     */
    var $_viewer;


    /**
     * The $mime_part class variable has the information to render
     * out, encapsulated in a MIME_Part object.
     *
     * @access public
     *
     * @param $params mixed  The parameters (if any) to pass to the underlying
     *                       MIME_Viewer.
     *
     * @return string  Rendering of the content.
     */
    function render($params = array())
    {
        /* Get the appropriate MIME_Viewer for the protocol specified. */
        if (!($this->_resolveViewer())) {
            return;
        }

        /* Render using the loaded MIME_Viewer object. */
        return $this->_viewer->render($params);
    }

    /**
     * Returns the content-type of the Viewer used to view the part.
     *
     * @access public
     *
     * @return string  A content-type string.
     */
    function getType()
    {
        /* Get the appropriate MIME_Viewer for the protocol specified. */
        if (!($this->_resolveViewer())) {
            return 'application/octet-stream';
        } else {
            return $this->_viewer->getType();
        }
    }

    /**
     * Load a MIME_Viewer according to the protocol parameter stored
     * in the MIME_Part to render. If unsuccessful, try to load a generic
     * multipart MIME_Viewer.
     *
     * @access private
     *
     * @return boolean  True on success, false on failure.
     */
    function _resolveViewer()
    {
        $viewer = null;

        if (empty($this->_viewer)) {
            $protocol = $this->mime_part->getContentTypeParameter('protocol');
            if (empty($protocol)) {
                return false;
            }
            $viewer = &MIME_Viewer::factory($this->mime_part, $protocol);
            if (empty($viewer) ||
                (get_class($viewer) == 'mime_viewer_default')) {
                $viewer = &MIME_Viewer::factory($this->mime_part, $this->mime_part->getPrimaryType() . '/*');
                if (empty($viewer)) {
                    return false;
                }
            }
            $this->_viewer = $viewer;
        }

        return true;
    }

}

--- NEW FILE: source.php ---
<?php
/**
 * The MIME_Viewer_source class is a class for any viewer that wants
 * to provide line numbers to extend.
 *
 * $Horde: framework/MIME/MIME/Viewer/source.php,v 1.8 2004/05/22 03:06:23 chuck Exp $
 *
 * Copyright 1999-2004 Chuck Hagenbuch <chuck at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_source extends MIME_Viewer {

    /**
     * Add line numbers to a block of code.
     *
     * @param string $code  The code to number.
     */
    function lineNumber($code, $linebreak = "\n")
    {
        $lines = substr_count($code, $linebreak) + 1;
        $html  = '<table style="border: 1px solid black" cellspacing="0" cellpadding="0" width="100%">';
        $html .= '<tr><td valign="top" style="background-color:#e9e9e9; padding-left:10px; padding-right:10px; text-align:right;">';
        for ($l = 1; $l <= $lines; $l++) {
            $html .= sprintf('<a style="font-family:monospace; font-size:12px;" name="%s" href="#%s">%s</a><br />', $l, $l, $l) . "\n";
        }
        $html .= '</td><td width="100%" valign="top" nowrap="nowrap" style="background-color:white; padding-left:10px; font-family:monospace; font-size:12px; white-space:pre;">' . $code . '</td>';
        return $html . '</tr></table>';
    }

}

--- NEW FILE: srchighlite.php ---
<?php

require_once dirname(__FILE__) . '/source.php';

/**
 * The MIME_Viewer_srchighlite class renders out various content
 * in HTML format by using Source-highlight.
 *
 * Web C Plus plus: http://www.gnu.org/software/src-highlite/
 *
 * $Horde: framework/MIME/MIME/Viewer/srchighlite.php,v 1.12 2004/05/22 03:06:24 chuck Exp $
 *
 * Copyright 2003-2004 Mike Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Mike Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_srchighlite extends MIME_Viewer_source {

    /**
     * Render out the currently set contents using Web C Plus Plus.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        global $mime_drivers, $registry;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['srchighlite']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['srchighlite']['location']) . '</pre>';
        }

        /* Create temporary files for Webcpp. */
        $tmpin  = Horde::getTempFile('SrcIn');
        $tmpout = Horde::getTempFile('SrcOut', false);

        /* Write the contents of our buffer to the temporary input file. */
        $contents = $this->mime_part->getContents();
        $fh = fopen($tmpin, 'wb');
        fwrite($fh, $contents, strlen($contents));
        fclose($fh);

        /* Determine the language from the mime type. */
        $lang = '';
        switch ($this->mime_part->getType()) {
        case 'text/x-java':
            $lang = 'java';
            break;

        case 'text/x-csrc':
        case 'text/x-c++src':
        case 'text/cpp':
            $lang = 'cpp';
            break;

        case 'application/x-perl':
            $lang = 'perl';
            break;

        case 'application/x-php':
        case 'x-extension/phps':
        case 'x-extension/php3s':
        case 'application/x-httpd-php':
        case 'application/x-httpd-php3':
        case 'application/x-httpd-phps':
            $lang = 'php3';
            break;

        case 'application/x-python':
            $lang = 'python';
            break;

            // $lang = 'prolog';
            // break;

            // $lang = 'flex';
            // break;

            // $lang = 'changelog';
            // break;

            // $lang = 'ruby';
            // break;
        }

        // Educated Guess at whether we are inline or not
        $inline = headers_sent();
        $html = '';

        if (!$inline) {
            $html .= Util::bufferOutput('require', $registry->getParam('templates', 'horde') . '/common-header.inc');
            $html .= "<div style='background-color:white'>";
        }

        /* Execute Source-Highlite. */
        exec($mime_drivers['horde']['srchighlite']['location'] . " --src-lang $lang --out-format xhtml --input $tmpin --output $tmpout");
        $fp = @fopen($tmpout, 'r');
        $data = @fread($fp, filesize($tmpout));
        @fclose($fp);
        unlink($tmpout);

        $html .= $this->lineNumber($this->lineNumber($data));

        if (!$inline) {
            $html .= '</div>';
            $html .= Util::bufferOutput('require', $registry->getParam('templates', 'horde') . '/common-footer.inc');
        }

        return $html;
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output.
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: tgz.php ---
<?php
/**
 * The MIME_Viewer_tgz class renders out plain or gzipped tarballs in HTML.
 *
 * $Horde: framework/MIME/MIME/Viewer/tgz.php,v 1.36 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 1999-2004 Anil Madhavapeddy <anil at recoil.org>
 * Copyright 2002-2004 Michael Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Anil Madhavapeddy <anil at recoil.org>
 * @author  Michael Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 1.3
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_tgz extends MIME_Viewer {

    /**
     * Render out the currently set tar file contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        require_once 'Horde/Compress.php';

        $contents = $this->mime_part->getContents();

        /* Only decompress gzipped files. */
        $subtype = $this->mime_part->getSubType();
        if (($subtype == 'x-gzip-compressed') ||
            ($subtype == 'tgz') ||
            ($subtype == 'x-tgz') ||
            ($subtype == 'x-gzip') ||
            ($subtype == 'x-gtar')) {
            $gzip = &Horde_Compress::singleton('gzip');
            $contents = $gzip->decompress($contents);
            if (empty($contents)) {
                return '<pre>' . _("Unable to open compressed archive.") . '</pre>';
            } elseif (is_a($contents, 'PEAR_Error')) {
                return '<pre>' . $contents->getMessage() . '</pre>';
            }
        }

        /* Obtain the list of files/data in the tar file. */
        $tar = &Horde_Compress::singleton('tar');

        $tarData = &$tar->decompress($contents);
        if (is_a($tarData, 'PEAR_Error')) {
            return '<pre>' . $tarData->getMessage() . '</pre>';
        }

        $fileCount = count($tarData);

        include_once 'Horde/Text.php';

        $text  = '<b>' . htmlspecialchars(sprintf(_("Contents of '%s'"), $this->mime_part->getName())) . ':</b>' . "\n";
        $text .= '<table><tr><td align="left"><tt><span class="fixed">';
        $text .= Text::htmlAllSpaces(_("Archive Name") . ':  ' . $this->mime_part->getName()) . "\n";
        $text .= Text::htmlAllSpaces(_("Archive File Size") . ': ' . strlen($contents) . ' bytes') . "\n";
        $text .= Text::htmlAllSpaces(($fileCount != 1) ? sprintf(_("File Count: %s files"), $fileCount) : sprintf(_("File Count: %s file"), $fileCount));
        $text .= "\n\n";
        $text .= Text::htmlAllSpaces(
                     str_pad(_("File Name"),     62, ' ', STR_PAD_RIGHT) .
                     str_pad(_("Attributes"),    15, ' ', STR_PAD_LEFT) .
                     str_pad(_("Size"),          10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Modified Date"), 19, ' ', STR_PAD_LEFT)
                 ) . "\n";

        $text .= str_repeat('-', 106) . "\n";

        foreach ($tarData as $val) {
           $text .= Text::htmlAllSpaces(
                        str_pad($val['name'], 62, ' ', STR_PAD_RIGHT) .
                        str_pad($val['attr'], 15, ' ', STR_PAD_LEFT) .
                        str_pad($val['size'], 10, ' ', STR_PAD_LEFT) .
                        str_pad(strftime("%d-%b-%Y %H:%M", $val['date']), 19, ' ', STR_PAD_LEFT)
                    ) . "\n";
        }

        $text .= str_repeat('-', 106) . "\n";
        $text .= '</span></tt></td></tr></table>';

        return nl2br($text);
    }

    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: tnef.php ---
<?php
/**
 * The MIME_Viewer_tnef class allows MS-TNEF attachments to be displayed.
 *
 * $Horde: framework/MIME/MIME/Viewer/tnef.php,v 1.13 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 2002-2004 Jan Schneider <jan at horde.org>
 * Copyright 2002-2004 Michael Slusarz <slusarz at bigworm.colorado.edu>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jan Schneider <jan at horde.org>
 * @author  Michael Slusarz <slusarz at bigworm.colorado.edu>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_tnef extends MIME_Viewer {

    /**
     * Render out the current tnef data.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        require_once 'Horde/Compress.php';

        $tnef = &Horde_Compress::singleton('tnef');

        $data = '<table border="1">';
        $info = $tnef->decompress($this->mime_part->getContents());
        if (empty($info) || is_a($info, 'PEAR_Error')) {
            $data .= '<tr><td>' . _("MS-TNEF Attachment contained no data.") . '</td></tr>';
        } else {
            $data .= '<tr><td>' . _("Name") . '</td><td>' . _("Mime Type") . '</td></tr>';
            foreach ($info as $part) {
                $data .= '<tr><td>' . $part['name'] . '</td><td>' . $part['type'] . '/' . $part['subtype'] . '</td></tr>';
            }
        }
        $data .= '</table>';

        return $data;
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: vcard.php ---
<?php
/**
 * The MIME_Viewer_vcard class renders out vCards in HTML format.
 *
 * $Horde: framework/MIME/MIME/Viewer/vcard.php,v 1.26 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 2002-2004 Jan Schneider <jan at horde.org>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Jan Schneider <jan at horde.org>
 * @version $Revision: 1.1 $
 * @since   Horde 2.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_vcard extends MIME_Viewer {

    /**
     * Render out the vcard contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = null)
    {
        global $registry, $prefs;

        require_once 'Horde/Data.php';

        $app = false;
        $data = $this->mime_part->getContents();
        $html = '';
        $import_msg = null;
        $title = _("vCard");
        $vc = &Horde_Data::singleton('vcard');

        $vc->importData($data);

        if (Util::getFormData('import') &&
            Util::getFormData('source') &&
            $registry->hasMethod('contacts/import_vcard')) {
            $source = Util::getFormData('source');
            $contacts = $registry->call('contacts/import_vcard', array($source, $data));
            if (is_a($contacts, 'PEAR_Error')) {
                $import_msg = _("There was an error importing the contact data.");
            } else {
                $import_msg = _("This contact data has been successfully added to your address book. Click below to view:");
                $import_msg .= '</td></tr>';
                foreach ($contacts as $contactID => $name) {
                    $url = Horde::url($registry->link('contacts/show', array('source' => $source, 'key' => $contactID)));
                    $import_msg .= '<tr><td class="item" colspan="2">';
                    $import_msg .= Horde::link($url, $name, null, '_blank') . Horde::img('mime/vcard.gif', $name, null, $registry->getParam('graphics', 'horde')) . ' ' . $name . '</a><br />';
                    $import_msg .= '</td></tr>';
                }
            }
        }

        $html  = Util::bufferOutput('include', $registry->getParam('templates', 'horde') . '/common-header.inc');
        $html .= '<table cellspacing="1" border="0" cellpadding="1">';
        if (!is_null($import_msg)) {
            $html .= '<tr><td colspan="2" class="header">' . $import_msg . '</td></tr><tr><td> </td></tr>';
        } elseif ($registry->hasMethod('contacts/import_vcard') &&
                  $registry->hasMethod('contacts/sources')) {
            $html .= '<tr><td colspan="2" class="header"><form action="' . $_SERVER['PHP_SELF'] . '" method="get" name="import_vcard">' . Util::formInput();
            foreach ($_GET as $key => $val) {
                $html .= '<input type="hidden" name="' . htmlspecialchars($key) . '" value="' . htmlspecialchars($val) . '" />';
            }
            $html .= '<input type="submit" class="button" name="import" value="' . _("Add to my address book:") . '" />';
            $html .= '<select name="source">';
            foreach ($registry->call('contacts/sources', array(true)) as $key => $label) {
                $selected = ($key == $prefs->getValue('add_source')) ? ' selected="selected"' : '';
                $html .= '<option value="' . htmlspecialchars($key) . '"' . $selected . '>' . htmlspecialchars($label) . '</option>';
            }
            $html .= '</form></td></tr><tr><td> </td></tr>';
        }

        for ($i = 0; $i < count($vc->count()); $i++) {
            if ($i > 0) {
                $html .= '<tr><td> </td></tr>';
            }

            $html .= '<tr><td colspan="2" class="header">';
            $fullname = $vc->getValues('FN', $i);
            if (array_key_exists(0, $fullname)) {
                $html .= $fullname[0]['value'];
            }
            $html .= '</td></tr>';

            $name = $vc->getValues('N', $i);
            $name_arr = array();
            if (array_key_exists(0, $name)) {
                $name_parts = explode(';', $name[0]['value']);
                if (isset($name_parts[3])) {
                    $name_arr[] = $name_parts[3];
                }
                if (isset($name_parts[1])) {
                    $name_arr[] = $name_parts[1];
                }
                if (isset($name_parts[2])) {
                    $name_arr[] = $name_parts[2];
                }
                if (isset($name_parts[0])) {
                    $name_arr[] = $name_parts[0];
                }
                if (isset($name_parts[4])) {
                    $name_arr[] = $name_parts[4];
                }
            }

            $html .= $this->_row(_("Name"), implode(' ', $name_arr));

            $aliases = $vc->getValues('ALIAS', $i);
            if (count($aliases)) {
                $alias_arr = array();
                foreach ($aliases as $alias) {
                    $alias_arr[] = $alias['value'];
                }
                $html .= $this->_row(_("Alias"), implode('<br />', $alias_arr));
            }

            $birthdays = $vc->getValues('BDAY', $i);
            if (count($birthdays)) {
                include_once 'Date/Calc.php';
                $birthday = $vc->mapDate($birthdays[0]['value']);
                $html .= $this->_row(_("Birthday"), Date_Calc::dateFormat($birthday['mday'], $birthday['month'], $birthday['year'], '%Y-%m-%d'));
            }

            $labels = $vc->getValues('LABEL', $i);
            foreach ($labels as $label) {
                if (isset($label['params']['TYPE'])) {
                    foreach ($label['params']['TYPE'] as $type) {
                        $label['params'][String::upper($type)] = true;
                    }
                }
                if (isset($label['params']['HOME'])) {
                    $html .= $this->_row(_("Home Address"), nl2br($label['value']));
                } elseif (isset($label['params']['WORK'])) {
                    $html .= $this->_row(_("Work Address"), nl2br($label['value']));
                } else {
                    $html .= $this->_row(_("Address"), nl2br($label['value']));
                }
            }

            $numbers = $vc->getValues('TEL', $i);
            foreach ($numbers as $number) {
                if (isset($number['params']['TYPE'])) {
                    foreach ($number['params']['TYPE'] as $type) {
                        $number['params'][String::upper($type)] = true;
                    }
                }
                if (isset($number['params']['VOICE'])) {
                    if (isset($number['params']['HOME'])) {
                        $html .= $this->_row(_("Home Phone"), $number['value']);
                    } elseif (isset($number['params']['WORK'])) {
                        $html .= $this->_row(_("Work Phone"), $number['value']);
                    } elseif (isset($number['params']['CELL'])) {
                        $html .= $this->_row(_("Cell Phone"), $number['value']);
                    } else {
                        $html .= $this->_row(_("Phone"), $number['value']);
                    }
                } elseif (isset($number['params']['FAX'])) {
                    $html .= $this->_row(_("Fax"), $number['value']);
                }
            }

            $addresses = $vc->getValues('EMAIL', $i);
            $emails = array();
            foreach ($addresses as $address) {
                if (isset($address['params']['TYPE'])) {
                    foreach ($address['params']['TYPE'] as $type) {
                        $address['params'][String::upper($type)] = true;
                    }
                }
                $email = '<a href="';
                if ($registry->hasMethod('mail/compose')) {
                    $email .= $registry->call('mail/compose', array(array('to' => $address['value'])));
                } else {
                    $email .= 'mailto:' . $address['value'];
                }
                $email .= '">' . $address['value'] . '</a>';
                if (isset($address['params']['PREF'])) {
                    array_unshift($emails, $email);
                } else {
                    array_push($emails, $email);
                }
            }
            if (count($emails)) {
                $html .= $this->_row(_("Email"), implode('<br />', $emails));
            }

            $title = $vc->getValues('TITLE', $i);
            if (count($title)) {
                $html .= $this->_row(_("Title"), $title[0]['value']);
            }

            $role = $vc->getValues('ROLE', $i);
            if (count($role)) {
                $html .= $this->_row(_("Role"), $role[0]['value']);
            }

            $org = $vc->getValues('ORG', $i);
            if (count($org)) {
                $html .= $this->_row(_("Company"), $org[0]['value']);
            }

            $notes = $vc->getValues('NOTE', $i);
            if (count($notes)) {
                $html .= $this->_row(_("Notes"), nl2br($notes[0]['value']));
            }

            $url = $vc->getValues('URL', $i);
            if (count($url)) {
                $html .= $this->_row(_("URL"), '<a href="' . $url[0]['value'] . '" target="_blank">' . $url[0]['value'] . '</a>');
            }

            $html .= '</table>';
        }

        $html .= Util::bufferOutput('include', $registry->getParam('templates', 'horde') . '/common-footer.inc');

        return $html;
    }

    function _row($label, $value)
    {
        return '<tr><td class="item" valign="top">' . $label . '</td><td class="item" valign="top">' . $value . "</td></tr>\n";
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output.
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: webcpp.php ---
<?php
/**
 * The MIME_Viewer_webcpp class renders out various content
 * in HTML format by using Web C Plus Plus.
 *
 * Web C Plus plus: http://webcpp.sourceforge.net/
 *
 * $Horde: framework/MIME/MIME/Viewer/webcpp.php,v 1.10 2004/04/16 17:21:45 jan Exp $
 *
 * Copyright 2002-2004 Mike Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Mike Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 3.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_webcpp extends MIME_Viewer {

    /**
     * Render out the currently set contents using Web C Plus Plus.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        global $mime_drivers;

        /* Check to make sure the program actually exists. */
        if (!file_exists($mime_drivers['horde']['webcpp']['location'])) {
            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $mime_drivers['horde']['webcpp']['location']) . '</pre>';
        }

        /* Create temporary files for Webcpp. */
        $tmpin  = Horde::getTempFile('WebcppIn');
        $tmpout = Horde::getTempFile('WebcppOut');

        /* Write the contents of our buffer to the temporary input file. */
        $contents = $this->mime_part->getContents();
        $fh = fopen($tmpin, 'wb');
        fwrite($fh, $contents, strlen($contents));
        fclose($fh);

        /* Get the extension for the mime type. */
        include_once 'Horde/MIME/Magic.php';
        $ext = MIME_Magic::MIMEToExt($this->mime_part->getType());

        /* Execute Web C Plus Plus. Specifying the in and out files didn't
           work for me but pipes did. */
        exec($mime_drivers['horde']['webcpp']['location'] . " --pipe --pipe -x=$ext < $tmpin > $tmpout");
        $results = file($tmpout);

        /* Extract the style sheet, removing any global body formatting
         * if we're displaying inline.
         */
        $res = preg_split('/(\<\/style\>)|(\<style type\=\"text\/css\"\>)/', implode('', $results));
        $style = $res[1];
        if ($this->canDisplayInline()) {
            $style = preg_replace('/\nbody\s+?{.*?}/s', '', $style);
        }

        /* Extract the content. */
        $res = preg_split('/\<\/?pre\>/', implode('', $results));
        $body = $res[1];
         
        return '<style>' . $style . '</style><pre><div class="webcpp">' . $body. '</div></pre>';
    }

    /**
     * Return the MIME content type of the rendered content.
     *
     * @access public
     *
     * @return string  The content type of the output. 
     */
    function getType()
    {
        return 'text/html';
    }

}

--- NEW FILE: zip.php ---
<?php
/**
 * The MIME_Viewer_zip class renders out the contents of ZIP files in HTML
 * format.
 *
 * $Horde: framework/MIME/MIME/Viewer/zip.php,v 1.29 2004/04/07 14:43:10 chuck Exp $
 *
 * Copyright 2000-2004 Chuck Hagenbuch <chuck at horde.org>
 * Copyright 2002-2004 Michael Cochrane <mike at graftonhall.co.nz>
 *
 * See the enclosed file COPYING for license information (LGPL). If you
 * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
 *
 * @author  Chuck Hagenbuch <chuck at horde.org>
 * @author  Michael Cochrane <mike at graftonhall.co.nz>
 * @version $Revision: 1.1 $
 * @since   Horde 2.0
 * @package Horde_MIME_Viewer
 */
class MIME_Viewer_zip extends MIME_Viewer {

    /**
     * Render out the current zip contents.
     *
     * @access public
     *
     * @param optional array $params  Any parameters the Viewer may need.
     *
     * @return string  The rendered contents.
     */
    function render($params = array())
    {
        return $this->_render($this->mime_part->getContents());
    }

    /**
     * Output the file list.
     *
     * @access private
     *
     * @param string $contents          The contents of the zip archive.
     * @param optional mixed $callback  The callback function to use on the
     *                                  zipfile information.
     *
     * @return string  The file list.
     */
    function _render($contents, $callback = null)
    {
        require_once 'Horde/Compress.php';

        $zip = &Horde_Compress::singleton('zip');

        /* Make sure this is a valid zip file. */
        if ($zip->checkZipData($contents) === false) {
            return '<pre>' . _("This does not appear to be a valid zip file.") . '</pre>';
        }

        $zipInfo = &$zip->decompress($contents, array('action' => HORDE_COMPRESS_ZIP_LIST));
        $fileCount = count($zipInfo);
 
        require_once 'Horde/Text.php';

        $text = '<b>' . htmlspecialchars(sprintf(_("Contents of '%s'"), $this->mime_part->getName())) . ':</b>' . "\n";
        $text .= '<table><tr><td align="left"><tt><span class="fixed">';
        $text .= Text::htmlAllSpaces(_("Archive Name") . ': ' . $this->mime_part->getName()) . "\n";
        $text .= Text::htmlAllSpaces(_("Archive File Size") . ': ' . strlen($contents) . ' bytes') . "\n";
        $text .= Text::htmlAllSpaces(($fileCount != 1) ? sprintf(_("File Count: %s files"), $fileCount) : sprintf(_("File Count: %s file"), $fileCount));
        $text .= "\n\n";
        $text .= Text::htmlAllSpaces(
                     str_pad(_("File Name"),      50, ' ', STR_PAD_RIGHT) .
                     str_pad(_("Attributes"),     10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Size") ,          10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Modified Date") , 19, ' ', STR_PAD_LEFT) .
                     str_pad(_("Method") ,        10, ' ', STR_PAD_LEFT) .
                     str_pad(_("CRC") ,           10, ' ', STR_PAD_LEFT) .
                     str_pad(_("Ratio") ,          7, ' ', STR_PAD_LEFT)
                 ) . "\n";

        $text .= str_repeat('-', 116) . "\n";

        foreach ($zipInfo as $key => $val) {
            $ratio = (empty($val['size'])) ? 0 : 100 * ($val['csize'] / $val['size']);

            $val['name'] = str_pad($val['name'], 50, ' ', STR_PAD_RIGHT);
            $val['attr'] = str_pad($val['attr'], 10, ' ', STR_PAD_LEFT);
            $val['size'] = str_pad($val['size'], 10, ' ', STR_PAD_LEFT); 
            $val['date'] = str_pad(strftime("%d-%b-%Y %H:%M", $val['date']), 19, ' ', STR_PAD_LEFT);
            $val['method'] = str_pad($val['method'], 10, ' ', STR_PAD_LEFT);
            $val['crc'] = str_pad($val['crc'], 10, ' ', STR_PAD_LEFT);
            $val['ratio'] = str_pad(sprintf("%1.1f%%", $ratio), 7, ' ', STR_PAD_LEFT);

            $val = array_map(array('Text', 'htmlAllSpaces'), $val);

            if (!is_null($callback)) {
                $val = call_user_func($callback, $key, $val);
            }

            $text .= $val['name'] . $val['attr'] . $val['size'] .
                     $val['date'] . $val['method'] . $val['crc'] .
                     $val['ratio'] . "\n";
        }

        $text .= str_repeat('-', 116) . "\n";
        $text .= '</span></tt></td></tr></table>';

        return nl2br($text);
    }

    /**
     * Return the content-type
     * 
     * @access public
     *
     * @return string  The content-type of the output.
     */ 
    function getType()
    {
        return 'text/html';
    }

}





More information about the commits mailing list