try to sort out renaming of files just involving case
authorChuck Hagenbuch <chuck@horde.org>
Sun, 9 Nov 2008 04:34:06 +0000 (23:34 -0500)
committerChuck Hagenbuch <chuck@horde.org>
Sun, 9 Nov 2008 04:34:06 +0000 (23:34 -0500)
55 files changed:
framework/mime_tmp/Horde/Mime.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Address.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Headers.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Magic.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Mail.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Mdn.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Message.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Part.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/Driver.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/audio.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/css.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/deb.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/default.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/enriched.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/enscript.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/html.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/images.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/msexcel.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/mspowerpoint.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/msword.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/common.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/global_document.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/main_html.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/palm.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/style_header.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/style_inlined.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/style_mapping.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/table.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/table_cells.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/table_columns.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/ooo/table_rows.xsl [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/pdf.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/php.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/plain.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/rar.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/report.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/rfc822.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/richtext.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/rpm.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/rtf.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/security.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/simple.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/smil.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/source.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/srchighlite.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/tgz.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/tnef.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/vcard.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/webcpp.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/wordperfect.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/Viewer/zip.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/mime.magic.php [new file with mode: 0644]
framework/mime_tmp/Horde/Mime/mime.mapping.php [new file with mode: 0644]

diff --git a/framework/mime_tmp/Horde/Mime.php b/framework/mime_tmp/Horde/Mime.php
new file mode 100644 (file)
index 0000000..9569c78
--- /dev/null
@@ -0,0 +1,443 @@
+<?php
+/**
+ * The MIME:: class provides methods for dealing with various MIME (see, e.g.,
+ * RFC 2045) standards.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@horde.org>
+ * @author  Michael Slusarz <slusarz@curecanti.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME
+{
+    /**
+     * Determines if a string contains 8-bit (non US-ASCII) characters.
+     *
+     * @param string $string   The string to check.
+     * @param string $charset  The charset of the string. Defaults to
+     *                         US-ASCII.
+     *
+     * @return boolean  True if string contains non US-ASCII characters.
+     */
+    static public function is8bit($string, $charset = null)
+    {
+        /* ISO-2022-JP is a 7bit charset, but it is an 8bit representation so
+         * it needs to be entirely encoded. */
+        return is_string($string) &&
+               ((stristr('iso-2022-jp', $charset) &&
+                (strstr($string, "\x1b\$B"))) ||
+                preg_match('/[\x80-\xff]/', $string));
+    }
+
+    /**
+     * Encodes a string containing non-ASCII characters according to RFC 2047.
+     *
+     * @param string $text     The text to encode.
+     * @param string $charset  The character set of the text.
+     *
+     * @return string  The text, encoded only if it contains non-ASCII
+     *                 characters.
+     */
+    static public function encode($text, $charset = null)
+    {
+        if (is_null($charset)) {
+            require_once 'Horde/NLS.php';
+            $charset = NLS::getCharset();
+        }
+        $charset = String::lower($charset);
+
+        if (($charset == 'us-ascii') || !self::is8bit($text, $charset)) {
+            return $text;
+        }
+
+        /* Get the list of elements in the string. */
+        $size = preg_match_all('/([^\s]+)([\s]*)/', $text, $matches, PREG_SET_ORDER);
+
+        $line = '';
+
+        /* Return if nothing needs to be encoded. */
+        foreach ($matches as $key => $val) {
+            if (self::is8bit($val[1], $charset)) {
+                if ((($key + 1) < $size) &&
+                    self::is8bit($matches[$key + 1][1], $charset)) {
+                    $line .= self::_encode($val[1] . $val[2], $charset) . ' ';
+                } else {
+                    $line .= self::_encode($val[1], $charset) . $val[2];
+                }
+            } else {
+                $line .= $val[1] . $val[2];
+            }
+        }
+
+        return rtrim($line);
+    }
+
+    /**
+     * Internal recursive function to RFC 2047 encode a string.
+     *
+     * @param string $text     The text to encode.
+     * @param string $charset  The character set of the text.
+     *
+     * @return string  The text, encoded only if it contains non-ASCII
+     *                 characters.
+     */
+    static protected function _encode($text, $charset)
+    {
+        $encoded = trim(base64_encode($text));
+        $c_size = strlen($charset) + 7;
+
+        if ((strlen($encoded) + $c_size) > 75) {
+            $parts = explode("\r\n", rtrim(chunk_split($encoded, intval((75 - $c_size) / 4) * 4)));
+        } else {
+            $parts[] = $encoded;
+        }
+
+        $p_size = count($parts);
+        $out = '';
+
+        foreach ($parts as $key => $val) {
+            $out .= '=?' . $charset . '?b?' . $val . '?=';
+            if ($p_size > $key + 1) {
+                /* RFC 2047 [2]: no encoded word can be more than 75
+                 * characters long. If longer, you must split the word with
+                 * CRLF SPACE. */
+                $out .= "\r\n ";
+            }
+        }
+
+        return $out;
+    }
+
+    /**
+     * Encodes a line via quoted-printable encoding.
+     *
+     * @param string $text   The text to encode.
+     * @param string $eol    The EOL sequence to use.
+     * @param integer $wrap  Wrap a line at this many characters.
+     *
+     * @return string  The quoted-printable encoded string.
+     */
+    static public function quotedPrintableEncode($text, $eol, $wrap = 76)
+    {
+        $line = $output = '';
+        $curr_length = 0;
+
+        /* We need to go character by character through the data. */
+        for ($i = 0, $length = strlen($text); $i < $length; ++$i) {
+            $char = $text[$i];
+
+            /* If we have reached the end of the line, reset counters. */
+            if ($char == "\n") {
+                $output .= $eol;
+                $curr_length = 0;
+                continue;
+            } elseif ($char == "\r") {
+                continue;
+            }
+
+            /* Spaces or tabs at the end of the line are NOT allowed. Also,
+             * ASCII characters below 32 or above 126 AND 61 must be
+             * encoded. */
+            $ascii = ord($char);
+            if ((($ascii === 32) &&
+                 ($i + 1 != $length) &&
+                 (($text[$i + 1] == "\n") || ($text[$i + 1] == "\r"))) ||
+                (($ascii < 32) || ($ascii > 126) || ($ascii === 61))) {
+                $char_len = 3;
+                $char = '=' . String::upper(sprintf('%02s', dechex($ascii)));
+            } else {
+                $char_len = 1;
+            }
+
+            /* Lines must be $wrap characters or less. */
+            $curr_length += $char_len;
+            if ($curr_length > $wrap) {
+                $output .= '=' . $eol;
+                $curr_length = $char_len;
+            }
+            $output .= $char;
+        }
+
+        return $output;
+    }
+
+    /**
+     * Encodes a string containing email addresses according to RFC 2047.
+     *
+     * This differs from encode() because it keeps email addresses legal, only
+     * encoding the personal information.
+     *
+     * @param mixed $addresses   The email addresses to encode (either a
+     *                           string or an array of addresses).
+     * @param string $charset    The character set of the text.
+     * @param string $defserver  The default domain to append to mailboxes.
+     *
+     * @return string  The text, encoded only if it contains non-ASCII
+     *                 characters, or PEAR_Error on error.
+     */
+    static public function encodeAddress($addresses, $charset = null,
+                                         $defserver = null)
+    {
+        if (!is_array($addresses)) {
+            /* parseAddressList() does not process the null entry
+             * 'undisclosed-recipients:;' correctly. */
+            $addresses = trim($addresses);
+            if (preg_match('/undisclosed-recipients:\s*;/i', $addresses)) {
+                return $addresses;
+            }
+
+            $addresses = Horde_MIME_Address::parseAddressList($addresses, array('defserver' => $defserver, 'nestgroups' => true));
+            if (is_a($addresses, 'PEAR_Error')) {
+                return $addresses;
+            }
+        }
+
+        $text = '';
+        foreach ($addresses as $addr) {
+            // Check for groups.
+            if (empty($addr['groupname'])) {
+                if (empty($addr['personal'])) {
+                    $personal = '';
+                } else {
+                    if (($addr['personal'][0] == '"') &&
+                        (substr($addr['personal'], -1) == '"')) {
+                        $addr['personal'] = stripslashes(substr($addr['personal'], 1, -1));
+                    }
+                    $personal = self::encode($addr['personal'], $charset);
+                }
+                $text .= Horde_MIME_Address::writeAddress($addr['mailbox'], $addr['host'], $personal) . ', ';
+            } else {
+                $text .= Horde_MIME_Address::writeGroupAddress($addr['groupname'], $addr['addresses']) . ' ';
+            }
+        }
+
+        return rtrim($text, ' ,');
+    }
+
+    /**
+     * Decodes an RFC 2047-encoded string.
+     *
+     * @param string $string      The text to decode.
+     * @param string $to_charset  The charset that the text should be decoded
+     *                            to.
+     *
+     * @return string  The decoded text.
+     */
+    static public function decode($string, $to_charset = null)
+    {
+        if (($pos = strpos($string, '=?')) === false) {
+            return $string;
+        }
+
+        /* Take out any spaces between multiple encoded words. */
+        $string = preg_replace('|\?=\s+=\?|', '?==?', $string);
+
+        /* Save any preceding text. */
+        $preceding = substr($string, 0, $pos);
+
+        $search = substr($string, $pos + 2);
+        $d1 = strpos($search, '?');
+        if ($d1 === false) {
+            return $string;
+        }
+
+        $charset = substr($string, $pos + 2, $d1);
+        $search = substr($search, $d1 + 1);
+
+        $d2 = strpos($search, '?');
+        if ($d2 === false) {
+            return $string;
+        }
+
+        $encoding = substr($search, 0, $d2);
+        $search = substr($search, $d2 + 1);
+
+        $end = strpos($search, '?=');
+        if ($end === false) {
+            $end = strlen($search);
+        }
+
+        $encoded_text = substr($search, 0, $end);
+        $rest = substr($string, (strlen($preceding . $charset . $encoding . $encoded_text) + 6));
+
+        if (is_null($to_charset)) {
+            require_once 'Horde/NLS.php';
+            $to_charset = NLS::getCharset();
+        }
+
+        switch ($encoding) {
+        case 'Q':
+        case 'q':
+            $decoded = preg_replace('/=([0-9a-f]{2})/ie', 'chr(0x\1)', str_replace('_', ' ', $encoded_text));
+            $decoded = String::convertCharset($decoded, $charset, $to_charset);
+            break;
+
+        case 'B':
+        case 'b':
+            $decoded = String::convertCharset(base64_decode($encoded_text), $charset, $to_charset);
+            break;
+
+        default:
+            $decoded = '=?' . $charset . '?' . $encoding . '?' . $encoded_text . '?=';
+            break;
+        }
+
+        return $preceding . $decoded . self::decode($rest, $to_charset);
+    }
+
+    /**
+     * Decodes an RFC 2047-encoded address string.
+     *
+     * @param string $string      The text to decode.
+     * @param string $to_charset  The charset that the text should be decoded
+     *                            to.
+     *
+     * @return string  The decoded text.
+     */
+    static public function decodeAddrString($string, $to_charset = null)
+    {
+        $addr_list = array();
+        foreach (Horde_MIME_Address::parseAddressList($string) as $ob) {
+            $ob['personal'] = isset($ob['personal'])
+                ? self::decode($ob['personal'], $to_charset)
+                : '';
+            $addr_list[] = $ob;
+        }
+
+        return Horde_MIME_Address::addrArray2String($addr_list);
+    }
+
+    /**
+     * Encodes a parameter string pursuant to RFC 2231.
+     *
+     * @param string $name     The parameter name.
+     * @param string $string   The string to encode.
+     * @param string $charset  The charset the text should be encoded with.
+     * @param string $lang     The language to use when encoding.
+     *
+     * @return array  The encoded parameter string.
+     */
+    static public function encodeParamString($name, $string, $charset,
+                                             $lang = null)
+    {
+        $encode = $wrap = false;
+        $output = array();
+
+        if (self::is8bit($string, $charset)) {
+            $string = String::lower($charset) . '\'' . (is_null($lang) ? '' : String::lower($lang)) . '\'' . rawurlencode($string);
+            $encode = true;
+        }
+
+        // 4 = '*', 2x '"', ';'
+        $pre_len = strlen($name) + 4 + (($encode) ? 1 : 0);
+        if (($pre_len + strlen($string)) > 76) {
+            while ($string) {
+                $chunk = 76 - $pre_len;
+                $pos = min($chunk, strlen($string) - 1);
+                if (($chunk == $pos) && ($pos > 2)) {
+                    for ($i = 0; $i <= 2; $i++) {
+                        if ($string[$pos-$i] == '%') {
+                            $pos -= $i + 1;
+                            break;
+                        }
+                    }
+                }
+                $lines[] = substr($string, 0, $pos + 1);
+                $string = substr($string, $pos + 1);
+            }
+            $wrap = true;
+        } else {
+            $lines = array($string);
+        }
+
+        $i = 0;
+        foreach ($lines as $val) {
+            $output[] =
+                $name .
+                (($wrap) ? ('*' . $i++) : '') .
+                (($encode) ? '*' : '') .
+                '="' . $val . '"';
+        }
+
+        return implode('; ', $output);
+    }
+
+    /**
+     * Decodes a parameter string encoded pursuant to RFC 2231.
+     *
+     * @param string $string      The entire string to decode, including the
+     *                            parameter name.
+     * @param string $to_charset  The charset the text should be decoded to.
+     *
+     * @return array  The decoded text, or the original string if it was not
+     *                encoded.
+     */
+    static public function decodeParamString($string, $to_charset = null)
+    {
+        if (($pos = strpos($string, '*')) === false) {
+            return false;
+        }
+
+        if (!isset($to_charset)) {
+            require_once 'Horde/NLS.php';
+            $to_charset = NLS::getCharset();
+        }
+
+        $attribute = substr($string, 0, $pos);
+        $charset = $lang = null;
+        $output = '';
+
+        /* Get the character set and language used in the encoding, if
+         * any. */
+        if (preg_match("/^[^=]+\*\=([^']*)'([^']*)'/", $string, $matches)) {
+            $charset = $matches[1];
+            $lang = $matches[2];
+            $string = str_replace($charset . "'" . $lang . "'", '', $string);
+        }
+
+        $lines = preg_split('/\s*' . preg_quote($attribute) . '(?:\*\d)*/', $string);
+        foreach ($lines as $line) {
+            if (strpos($line, '*=') === 0) {
+                $output .= urldecode(str_replace(array('_', '='), array('%20', '%'), substr($line, 2)));
+            } else {
+                $output .= substr($line, 1);
+            }
+        }
+
+        /* RFC 2231 uses quoted printable encoding. */
+        if (!is_null($charset)) {
+            $output = String::convertCharset($output, $charset, $to_charset);
+        }
+
+        return array(
+            'attribute' => $attribute,
+            'value' => $output
+        );
+    }
+
+    /**
+     * Generates a Message-ID string conforming to RFC 2822 [3.6.4] and the
+     * standards outlined in 'draft-ietf-usefor-message-id-01.txt'.
+     *
+     * @param string  A message ID string.
+     */
+    static public function generateMessageID()
+    {
+        return '<' . date('YmdHis') . '.' . self::generateRandomID() . '@' . $_SERVER['SERVER_NAME'] . '>';
+    }
+
+    /**
+     * Generates a Random-ID string suitable for use with MIME features that
+     * require a random string.
+     *
+     * @return string  A random string.
+     */
+    static public function generateRandomID()
+    {
+        return base_convert(dechex(strtr(microtime(), array('0.' => '', ' ' => ''))) . uniqid(), 16, 36);
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Address.php b/framework/mime_tmp/Horde/Mime/Address.php
new file mode 100644 (file)
index 0000000..1ca31fd
--- /dev/null
@@ -0,0 +1,401 @@
+<?php
+/**
+ * The Horde_MIME_Address:: class provides methods for dealing with email
+ * address standards (RFC 822/2822/5322).
+ *
+ * Copyright 2008 The Horde Project (http://www.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@horde.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Address
+{
+    /**
+     * Builds an RFC compliant email address.
+     *
+     * @param string $mailbox   Mailbox name.
+     * @param string $host      Domain name of mailbox's host.
+     * @param string $personal  Personal name phrase.
+     *
+     * @return string  The correctly escaped and quoted
+     *                 "$personal <$mailbox@$host>" string.
+     */
+    static public function writeAddress($mailbox, $host, $personal = '')
+    {
+        $address = '';
+
+        if (strlen($personal)) {
+            $address .= self::encode($personal, 'personal') . ' <';
+        }
+
+        $address .= self::encode($mailbox, 'address') . '@' . ltrim($host, '@');
+
+        if (strlen($personal)) {
+            $address .= '>';
+        }
+
+        return $address;
+    }
+
+    /**
+     * Write an RFC compliant group address, given the group name and a list
+     * of email addresses.
+     *
+     * @param string $groupname  The name of the group.
+     * @param array $addresses   The component email addresses. These e-mail
+     *                           addresses must be in RFC format.
+     *
+     * @return string  The correctly quoted group string.
+     */
+    static public function writeGroupAddress($groupname, $addresses = array())
+    {
+        return self::encode($groupname, 'address') . ':' . (empty($addresses) ? '' : implode(', ', $addresses)) . ';';
+    }
+
+    /**
+     * If an email address has no personal information, get rid of any angle
+     * brackets (<>) around it.
+     *
+     * @param string $address  The address to trim.
+     *
+     * @return string  The trimmed address.
+     */
+    static public function trimAddress($address)
+    {
+        $address = trim($address);
+
+        if (($address[0] == '<') && (substr($address, -1) == '>')) {
+            $address = substr($address, 1, -1);
+        }
+
+        return $address;
+    }
+
+    /**
+     * Explodes an RFC string, ignoring a delimiter if preceded by a "\"
+     * character, or if the delimiter is inside single or double quotes.
+     *
+     * @param string $string      The RFC compliant string.
+     * @param string $delimiters  A string containing valid delimiters.
+     *                            Defaults to ','.
+     *
+     * @return array  The exploded string in an array.
+     */
+    static public function explode($string, $delimiters = ',')
+    {
+        if (!strlen($string)) {
+            return array($string);
+        }
+
+        $emails = array();
+        $pos = 0;
+        $in_group = $in_quote = false;
+
+        for ($i = 0, $iMax = strlen($string); $i < $iMax; ++$i) {
+            $char = $string[$i];
+            if ($char == '"') {
+                if (!$i || ($prev !== '\\')) {
+                    $in_quote = !$in_quote;
+                }
+            } elseif ($in_group) {
+                if ($char == ';') {
+                    $emails[] = substr($string, $pos, $i - $pos + 1);
+                    $pos = $i + 1;
+                    $in_group = false;
+                }
+            } elseif (!$in_quote) {
+                if ($char == ':') {
+                    $in_group = true;
+                } elseif ((strpos($delimiters, $char) !== false) &&
+                          (!$i || ($prev !== '\\'))) {
+                    $emails[] = $i ? substr($string, $pos, $i - $pos) : '';
+                    $pos = $i + 1;
+                }
+            }
+            $prev = $char;
+        }
+
+        if ($pos != $i) {
+            /* The string ended without a delimiter. */
+            $emails[] = substr($string, $pos, $i - $pos);
+        }
+
+        return $emails;
+    }
+
+    /**
+     * Takes an address object array and formats it as a string.
+     *
+     * Object array format for the address "John Doe <john_doe@example.com>"
+     * is:
+     * <pre>
+     * 'personal' = Personal name ("John Doe")
+     * 'mailbox' = The user's mailbox ("john_doe")
+     * 'host' = The host the mailbox is on ("example.com")
+     * </pre>
+     *
+     * @param array $ob      The address object to be turned into a string.
+     * @param mixed $filter  A user@example.com style bare address to ignore.
+     *                       Either single string or an array of strings.  If
+     *                       the address matches $filter, an empty string will
+     *                       be returned.
+     *
+     * @return string  The formatted address.
+     */
+    static public function addrObject2String($ob, $filter = '')
+    {
+        /* If the personal name is set, decode it. */
+        $ob['personal'] = isset($ob['personal'])
+            ? Horde_MIME::decode($ob['personal'])
+            : '';
+
+        /* If both the mailbox and the host are empty, return an empty string.
+         * If we just let this case fall through, the call to writeAddress()
+         * will end up return just a '@', which is undesirable. */
+        if (empty($ob['mailbox']) && empty($ob['host'])) {
+            return '';
+        }
+
+        /* Make sure these two variables have some sort of value. */
+        if (!isset($ob['mailbox'])) {
+            $ob['mailbox'] = '';
+        } elseif ($ob['mailbox'] == 'undisclosed-recipients') {
+            return '';
+        }
+        if (!isset($ob['host'])) {
+            $ob['host'] = '';
+        }
+
+        /* Filter out unwanted addresses based on the $filter string. */
+        if ($filter) {
+            if (!is_array($filter)) {
+                $filter = array($filter);
+            }
+            foreach ($filter as $f) {
+                if (strcasecmp($f, $ob['mailbox'] . '@' . $ob['host']) == 0) {
+                    return '';
+                }
+            }
+        }
+
+        /* Return the formatted email address. */
+        return self::writeAddress($ob['mailbox'], $ob['host'], $ob['personal']);
+    }
+
+    /**
+     * Takes an array of address object arrays and passes each of them through
+     * addrObject2String().
+     *
+     * @param array $addresses  The array of address objects.
+     * @param mixed $filter     A user@example.com style bare address to
+     *                          ignore.  If any address matches $filter, it
+     *                          will not be included in the final string.
+     *
+     * @return string  All of the addresses in a comma-delimited string.
+     *                 Returns the empty string on error/no addresses found.
+     */
+    static public function addrArray2String($addresses, $filter = '')
+    {
+        if (!is_array($addresses)) {
+            return '';
+        }
+
+        $addrList = array();
+
+        foreach ($addresses as $addr) {
+            $val = self::addrObject2String($addr, $filter);
+            if (!empty($val)) {
+                $addrList[String::lower(self::bareAddress($val))] = $val;
+            }
+        }
+
+        return implode(', ', $addrList);
+    }
+
+    /**
+     * Return the list of addresses for a header object.
+     *
+     * @param array $obs  An array of header objects.
+     *
+     * @return array  An array of address information. Array elements:
+     * <pre>
+     * 'address' - (string) Full address
+     * 'display' - (string) A displayable version of the address
+     * 'groupname' - (string) The group name.
+     * 'host' - (string) Hostname
+     * 'inner' - (string) Trimmed, bare address
+     * 'personal' - (string) Personal string
+     * </pre>
+     */
+    static public function getAddressesFromObject($obs)
+    {
+        $ret = array();
+
+        if (!is_array($obs) || empty($obs)) {
+            return $ret;
+        }
+
+        foreach ($obs as $ob) {
+            if (isset($ob['groupname'])) {
+                $ret[] = array(
+                    'addresses' => self::getAddressesFromObject($ob['addresses']),
+                    'groupname' => $ob['groupname']
+                );
+                continue;
+            }
+
+            $ob = array_merge(array(
+                'host' => '',
+                'mailbox' => '',
+                'personal' => ''
+            ), $ob);
+
+            /* Ensure we're working with initialized values. */
+            if (!empty($ob['personal'])) {
+                $ob['personal'] = stripslashes(trim(Horde_MIME::decode($ob['personal']), '"'));
+            }
+
+            $inner = self::writeAddress($ob['mailbox'], $ob['host']);
+
+            /* Generate the new object. */
+            $ret[] = array(
+                'address' => self::addrObject2String($ob),
+                'display' => (empty($ob['personal']) ? '' : $ob['personal'] . ' <') . $inner . (empty($ob['personal']) ? '' : '>'),
+                'host' => $ob['host'],
+                'inner' => $inner,
+                'personal' => $ob['personal']
+            );
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the bare address.
+     *
+     * @param string $address    The address string.
+     * @param string $defserver  The default domain to append to mailboxes.
+     * @param boolean $multiple  Should we return multiple results?
+     *
+     * @return mixed  If $multiple is false, returns the mailbox@host e-mail
+     *                address.  If $multiple is true, returns an array of
+     *                these addresses.
+     */
+    static public function bareAddress($address, $defserver = null,
+                                       $multiple = false)
+    {
+        $addressList = array();
+
+        $from = self::parseAddressList($address, array('defserver' => $defserver));
+        if (is_a($from, 'PEAR_Error')) {
+            return $multiple ? array() : '';
+        }
+
+        foreach ($from as $entry) {
+            if (!empty($entry['mailbox'])) {
+                $addressList[] = $entry['mailbox'] . (isset($entry['host']) ? '@' . $entry['host'] : '');
+            }
+        }
+
+        return $multiple ? $addressList : array_pop($addressList);
+    }
+
+    /**
+     * Parses a list of email addresses into its parts. Handles distribution
+     * lists.
+     *
+     * @param string $address  The address string.
+     * @param array $options   Additional options:
+     * <pre>
+     * 'defserver' - (string) The default domain to append to mailboxes.
+     *               DEFAULT: No domain appended.
+     * 'nestgroups' - (boolean) Nest the groups? (Will appear under the
+     *                'groupname' key)
+     *                DEFAULT: No.
+     * 'reterror' - (boolean) Return a PEAR_Error object on error?
+     *              DEFAULT: Returns an empty array on error.
+     * 'validate' - (boolean) Validate the address(es)?
+     *              DEFAULT: No.
+     * </pre>
+     *
+     * @return mixed  If 'reterror' is true, returns a PEAR_Error object on
+     *                error.  Otherwise, a list of arrays with the possible
+     *                keys: 'mailbox', 'host', 'personal', 'adl', 'groupname',
+     *                and 'comment'.
+     */
+    static public function parseAddressList($address, $options = array())
+    {
+        if (preg_match('/undisclosed-recipients:\s*;/i', trim($address))) {
+            return array();
+        }
+
+        $options = array_merge(array(
+            'defserver' => null,
+            'nestgroups' => false,
+            'reterror' => false,
+            'validate' => false
+        ), $options);
+
+        static $parser;
+        if (!isset($parser)) {
+            require_once 'Mail/RFC822.php';
+            $parser = new Mail_RFC822();
+        }
+
+        $ret = $parser->parseAddressList($address, $options['defserver'], $options['nestgroups'], $options['validate']);
+        if (is_a($ret, 'PEAR_Error')) {
+            return empty($options['reterror']) ? array() : $ret;
+        }
+
+        /* Convert objects to arrays. */
+        foreach (array_keys($ret) as $key) {
+            $ret[$key] = (array) $ret[$key];
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Quotes and escapes the given string if necessary using rules contained
+     * in RFC 2822 [3.2.5].
+     *
+     * @param string $str   The string to be quoted and escaped.
+     * @param string $type  Either 'address' or 'personal'.
+     *
+     * @return string  The correctly quoted and escaped string.
+     */
+    static public function encode($str, $type = 'address')
+    {
+        // Excluded (in ASCII): 0-8, 10-31, 34, 40-41, 44, 58-60, 62, 64,
+        // 91-93, 127
+        $filter = "\0\1\2\3\4\5\6\7\10\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\"(),:;<>@[\\]\177";
+
+        switch ($type) {
+        case 'address':
+            // RFC 2822 [3.4.1]: (HTAB, SPACE) not allowed in address
+            $filter .= "\11\40";
+            break;
+
+        case 'personal':
+            // RFC 2822 [3.4]: Period not allowed in display name
+            $filter .= '.';
+            break;
+        }
+
+        // Strip double quotes if they are around the string already.
+        // If quoted, we know that the contents are already escaped, so
+        // unescape now.
+        $str = trim($str);
+        if ($str && ($str[0] == '"') && (substr($str, -1) == '"')) {
+            $str = stripslashes(substr($str, 1, -1));
+        }
+
+        return (strcspn($str, $filter) != strlen($str))
+            ? '"' . addcslashes($str, '\\"') . '"'
+            : $str;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Headers.php b/framework/mime_tmp/Horde/Mime/Headers.php
new file mode 100644 (file)
index 0000000..164f7de
--- /dev/null
@@ -0,0 +1,543 @@
+<?php
+/**
+ * The Horde_MIME_Headers:: class contains generic functions related to
+ * handling the headers of mail messages.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Headers
+{
+    /**
+     * The internal headers array.
+     *
+     * @var array
+     */
+    protected $_headers = array();
+
+    /**
+     * The sequence to use as EOL for the headers.
+     * The default is currently to output the EOL sequence internally as
+     * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045.
+     * To be RFC complaint, the full <CR><LF> EOL combination should be used
+     * when sending a message.
+     *
+     * @var string
+     */
+    protected $_eol = "\n";
+
+    /**
+     * The User-Agent string to use.
+     *
+     * @var string
+     */
+    protected $_agent = null;
+
+    /**
+     * Returns the internal header array in array format.
+     *
+     * @param array $options  Optional parameters:
+     * <pre>
+     * 'charset' => (string) Encodes the headers using this charset.
+     *              DEFAULT: No encoding.
+     * 'defserver' => (string) TODO
+     *              DEFAULT: NO
+     * 'nowrap' => (integer) Don't wrap the headers.
+     *             DEFAULT: Headers are wrapped.
+     * </pre>
+     *
+     * @return array  The headers in array format.
+     */
+    public function toArray($options = array())
+    {
+        $ret = array();
+        $address_keys = empty($options['charset'])
+            ? array()
+            : $this->addressFields();
+
+        foreach ($this->_headers as $header => $ob) {
+            $val = is_array($ob['value']) ? $ob['value'] : array($ob['value']);
+
+            foreach (array_keys($val) as $key) {
+                if (!empty($address_keys)) {
+                    if (in_array($header, $address_keys)) {
+                        $text = Horde_MIME::encodeAddress($val[$key], $charset, empty($options['defserver']) ? null : $options['defserver']);
+                                                                                                        if (is_a($text, 'PEAR_Error')) {
+                            $text = $val[$key];
+                        }
+                    } else {
+                        $text = Horde_MIME::encode($val[$key], $options['charset']);
+                    }
+                } else {
+                    $text = $val[$key];
+                }
+
+                if (empty($options['nowrap'])) {
+                    $text = $this->wrapHeaders($header, $text);
+                }
+
+                $val[$key] = $text;
+            }
+
+            $ret[$ob['header']] = (count($val) == 1) ? reset($val) : $val;
+        }
+
+        return $ret;
+    }
+
+    /**
+     * Returns the internal header array in string format.
+     *
+     * @param array $options  Optional parameters:
+     * <pre>
+     * 'charset' => (string) Encodes the headers using this charset.
+     *              DEFAULT: No encoding.
+     * 'defserver' => (string) TODO
+     *              DEFAULT: NO
+     * 'nowrap' => (integer) Don't wrap the headers.
+     *             DEFAULT: Headers are wrapped.
+     * </pre>
+     *
+     * @return string  The headers in string format.
+     */
+    public function toString($options = array())
+    {
+        $text = '';
+
+        foreach ($this->toArray($options) as $key => $val) {
+            if (!is_array($val)) {
+                $val = array($val);
+            }
+            foreach ($val as $entry) {
+                $text .= $key . ': ' . $entry . $this->_eol;
+            }
+        }
+
+        return $text . $this->_eol;
+    }
+
+    /**
+     * Generate the 'Received' header for the Web browser->Horde hop
+     * (attempts to conform to guidelines in RFC 5321 [4.4]).
+     */
+    public function addReceivedHeader()
+    {
+        $old_error = error_reporting(0);
+        if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+            /* This indicates the user is connecting through a proxy. */
+            $remote_path = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
+            $remote_addr = $remote_path[0];
+            $remote = gethostbyaddr($remote_addr);
+        } else {
+            $remote_addr = $_SERVER['REMOTE_ADDR'];
+            $remote = empty($_SERVER['REMOTE_HOST'])
+                ? gethostbyaddr($remote_addr)
+                : $_SERVER['REMOTE_HOST'];
+        }
+        error_reporting($old_error);
+
+        if (!empty($_SERVER['REMOTE_IDENT'])) {
+            $remote_ident = $_SERVER['REMOTE_IDENT'] . '@' . $remote . ' ';
+        } elseif ($remote != $_SERVER['REMOTE_ADDR']) {
+            $remote_ident = $remote . ' ';
+        } else {
+            $remote_ident = '';
+        }
+
+        if (!empty($GLOBALS['conf']['server']['name'])) {
+            $server_name = $GLOBALS['conf']['server']['name'];
+        } elseif (!empty($_SERVER['SERVER_NAME'])) {
+            $server_name = $_SERVER['SERVER_NAME'];
+        } elseif (!empty($_SERVER['HTTP_HOST'])) {
+            $server_name = $_SERVER['HTTP_HOST'];
+        } else {
+            $server_name = 'unknown';
+        }
+
+        $received = 'from ' . $remote . ' (' . $remote_ident .
+            '[' . $remote_addr . ']) ' .
+            'by ' . $server_name . ' (Horde Framework) with HTTP; ' .
+            date('r');
+
+        $this->addHeader('Received', $received);
+    }
+
+    /**
+     * Generate the 'Message-ID' header.
+     */
+    public function addMessageIdHeader()
+    {
+        require_once dirname(__FILE__) . '/../MIME.php';
+        $this->addHeader('Message-ID', Horde_MIME::generateMessageID());
+    }
+
+    /**
+     * Generate the 'Resent' headers (conforms to guidelines in
+     * RFC 2822 [3.6.6]).
+     *
+     * @param string $from  The address to use for 'Resent-From'.
+     * @param string $to    The address to use for 'Resent-To'.
+     */
+    public function addResentHeaders($from, $to)
+    {
+        require_once dirname(__FILE__) . '/../MIME.php';
+
+        /* We don't set Resent-Sender, Resent-Cc, or Resent-Bcc. */
+        $this->addHeader('Resent-Date', date('r'));
+        $this->addHeader('Resent-From', $from);
+        $this->addHeader('Resent-To', $to);
+        $this->addHeader('Resent-Message-ID', Horde_MIME::generateMessageID());
+    }
+
+    /**
+     * Generate the user agent description header.
+     */
+    public function addUserAgentHeader()
+    {
+        $this->addHeader('User-Agent', $this->getUserAgent());
+    }
+
+    /**
+     * Returns the user agent description header.
+     *
+     * @return string  The user agent header.
+     */
+    public function getUserAgent()
+    {
+        if (is_null($this->_agent)) {
+            $this->_agent = 'Horde Application Framework 4.0';
+        }
+        return $this->_agent;
+    }
+
+    /**
+     * Explicitly sets the User-Agent string.
+     *
+     * @param string $agent  The User-Agent string to use.
+     */
+    public function setUserAgent($agent)
+    {
+        $this->_agent = $agent;
+    }
+
+    /**
+     * Add a header to the header array.
+     *
+     * @param string $header   The header name.
+     * @param string $value    The header value.
+     * @param boolean $decode  MIME decode the value?
+     */
+    public function addHeader($header, $value, $decode = false)
+    {
+        require_once 'Horde/String.php';
+
+        $header = trim($header);
+        $lcHeader = String::lower($header);
+
+        if (!isset($this->_headers[$lcHeader])) {
+            $this->_headers[$lcHeader] = array();
+            $this->_headers[$lcHeader]['header'] = $header;
+        }
+        $ptr = &$this->_headers[$lcHeader];
+
+        if ($decode) {
+            require_once dirname(__FILE__) . '/../MIME.php';
+
+            // Fields defined in RFC 2822 that contain address information
+            if (in_array($lcHeader, $this->addressFields())) {
+                $value = Horde_MIME::decodeAddrString($value);
+            } else {
+                $value = Horde_MIME::decode($value);
+            }
+        }
+
+        if (isset($ptr['value'])) {
+            if (!is_array($ptr['value'])) {
+                $ptr['value'] = array($ptr['value']);
+            }
+            $ptr['value'][] = $value;
+        } else {
+            $ptr['value'] = $value;
+        }
+    }
+
+    /**
+     * Remove a header from the header array.
+     *
+     * @param string $header  The header name.
+     */
+    public function removeHeader($header)
+    {
+        require_once 'Horde/String.php';
+        unset($this->_headers[String::lower(trim($header))]);
+    }
+
+    /**
+     * Set a value for a particular header ONLY if that header is set.
+     *
+     * @param string $header   The header name.
+     * @param string $value    The header value.
+     * @param boolean $decode  MIME decode the value?
+     *
+     * @return boolean  True if value was set.
+     */
+    public function setValue($header, $value, $decode = false)
+    {
+        require_once 'Horde/String.php';
+
+        if (isset($this->_headers[String::lower($header)])) {
+            $this->addHeader($header, $value, $decode);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Attempts to return the header in the correct case.
+     *
+     * @param string $header  The header to search for.
+     *
+     * @return string  The value for the given header.
+     *                 If the header is not found, returns null.
+     */
+    public function getString($header)
+    {
+        require_once 'Horde/String.php';
+
+        $lcHeader = String::lower($header);
+        return (isset($this->_headers[$lcHeader]))
+            ? $this->_headers[$lcHeader]['header']
+            : null;
+    }
+
+    /**
+     * Attempt to return the value for a given header.
+     * The following header fields can only have 1 entry, so if duplicate
+     * entries exist, the first value will be used:
+     *   * To, From, Cc, Bcc, Date, Sender, Reply-to, Message-ID, In-Reply-To,
+     *     References, Subject (RFC 2822 [3.6])
+     *   * All List Headers (RFC 2369 [3])
+     *
+     * @param string $header  The header to search for.
+     *
+     * @return mixed  The value for the given header.
+     *                If the header is not found, returns null.
+     */
+    public function getValue($header)
+    {
+        require_once 'Horde/String.php';
+
+        $header = String::lower($header);
+
+        if (isset($this->_headers[$header])) {
+            return (is_array($this->_headers[$header]['value']) && in_array($header, $this->singleFields(true)))
+                ? $this->_headers[$header]['value'][0]
+                : $this->_headers[$header]['value'];
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the list of RFC defined header fields that contain address
+     * info.
+     *
+     * @return array  The list of headers, in lowercase.
+     */
+    public function addressFields()
+    {
+        return array(
+            'from', 'to', 'cc', 'bcc', 'reply-to', 'resent-to', 'resent-cc',
+            'resent-bcc', 'resent-from', 'sender'
+        );
+    }
+
+    /**
+     * Returns the list of RFC defined header fields that can only contain
+     * a single value.
+     *
+     * @param boolean $list  Return list-related headers also?
+     *
+     * @return array  The list of headers, in lowercase.
+     */
+    public function singleFields($list = true)
+    {
+        $single = array(
+            'to', 'from', 'cc', 'bcc', 'date', 'sender', 'reply-to',
+            'message-id', 'in-reply-to', 'references', 'subject', 'x-priority'
+        );
+
+        if ($list) {
+            $single = array_merge($single, array_keys($this->listHeaders()));
+        }
+
+        return $single;
+    }
+
+    /**
+     * Returns the list of valid mailing list headers.
+     *
+     * @return array  The list of valid mailing list headers.
+     */
+    public function listHeaders()
+    {
+        return array(
+            /* RFC 2369 */
+            'list-help'         =>  _("List-Help"),
+            'list-unsubscribe'  =>  _("List-Unsubscribe"),
+            'list-subscribe'    =>  _("List-Subscribe"),
+            'list-owner'        =>  _("List-Owner"),
+            'list-post'         =>  _("List-Post"),
+            'list-archive'      =>  _("List-Archive"),
+            /* RFC 2919 */
+            'list-id'           =>  _("List-Id")
+        );
+    }
+
+    /**
+     * Do any mailing list headers exist?
+     *
+     * @return boolean  True if any mailing list headers exist.
+     */
+    public function listHeadersExist()
+    {
+        return (bool) count(array_intersect(array_keys($this->listHeaders()), array_keys($this->_headers)));
+    }
+
+    /**
+     * Sets a new string to use for EOLs.
+     *
+     * @param string $eol  The string to use for EOLs.
+     */
+    public function setEOL($eol)
+    {
+        $this->_eol = $eol;
+    }
+
+    /**
+     * Get the string to use for EOLs.
+     *
+     * @return string  The string to use for EOLs.
+     */
+    public function getEOL()
+    {
+        return $this->_eol;
+    }
+
+    /**
+     * Returns a header from the header object.
+     *
+     * @param string $field  The header to return as an object.
+     *
+     * @return array  The object for the field requested.
+     */
+    public function getOb($field)
+    {
+        $val = $this->getValue($field);
+        return is_null($val)
+            ? array()
+            : Horde_MIME_Address::parseAddressList($val);
+    }
+
+    /**
+     * Adds proper linebreaks to a header string.
+     * RFC 2822 says headers SHOULD only be 78 characters a line, but also
+     * says that a header line MUST not be more than 998 characters.
+     *
+     * @param string $header  The header name.
+     * @param string $text    The text of the header field.
+     *
+     * @return string  The header value, with linebreaks inserted.
+     */
+    public function wrapHeaders($header, $text)
+    {
+        $eol = $this->_eol;
+        $header_text = rtrim($header) . ': ';
+
+        /* Remove any existing linebreaks. */
+        $text = $header_text . preg_replace("/\r?\n\s?/", ' ', rtrim($text));
+
+        if (!in_array(strtolower($header), array('content-type', 'content-disposition'))) {
+            /* Wrap the line. */
+            $line = wordwrap($text, 75, $eol . ' ');
+
+            /* Make sure there are no empty lines. */
+            $line = preg_replace('/' . $eol . ' ' . $eol . ' /', '/' . $eol . ' /', $line);
+
+            return substr($line, strlen($header_text));
+        }
+
+        /* Split the line by the RFC parameter separator ';'. */
+        $params = preg_split("/\s*;\s*/", $text);
+
+        $line = '';
+        $eollength = strlen($eol);
+        $length = 1000 - $eollength;
+        $paramcount = count($params);
+
+        reset($params);
+        while (list($count, $val) = each($params)) {
+            /* If longer than RFC allows, then simply chop off the excess. */
+            $moreparams = (($count + 1) != $paramcount);
+            $maxlength = $length - (!empty($line) ? 1 : 0) - (($moreparams) ? 1 : 0);
+            if (strlen($val) > $maxlength) {
+                $val = substr($val, 0, $maxlength);
+
+                /* If we have an opening quote, add a closing quote after
+                 * chopping the rest of the text. */
+                if (strpos($val, '"') !== false) {
+                    $val = substr($val, 0, -1) . '"';
+                }
+            }
+
+            if (!empty($line)) {
+                $line .= ' ';
+            }
+            $line .= $val . (($moreparams) ? ';' : '') . $eol;
+        }
+
+        return substr($line, strlen($header_text), ($eollength * -1));
+    }
+
+    /**
+     * Builds a Horde_MIME_Headers object from header text.
+     * This function can be called statically:
+     *   $headers = Horde_MIME_Headers::parseHeaders().
+     *
+     * @param string $text  A text string containing the headers.
+     *
+     * @return Horde_MIME_Headers  A new Horde_MIME_Headers object.
+     */
+    static public function parseHeaders($text)
+    {
+        $headers = new Horde_MIME_Headers();
+        $currheader = $currtext = null;
+
+        foreach (explode("\n", $text) as $val) {
+            $val = rtrim($val);
+            if (empty($val)) {
+                break;
+            }
+
+            if (($val[0] == ' ') || ($val[0] == "\t")) {
+                $currtext .= ' ' . ltrim($val);
+            } else {
+                if (!is_null($currheader)) {
+                    $headers->addHeader($currheader, $currtext, true);
+                }
+                $pos = strpos($val, ':');
+                $currheader = substr($val, 0, $pos);
+                $currtext = ltrim(substr($val, $pos + 1));
+            }
+        }
+        $headers->addHeader($currheader, $currtext, true);
+
+        return $headers;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Magic.php b/framework/mime_tmp/Horde/Mime/Magic.php
new file mode 100644 (file)
index 0000000..ef93b45
--- /dev/null
@@ -0,0 +1,260 @@
+<?php
+
+require_once 'Horde/Util.php';
+
+/**
+ * The Horde_MIME_Magic:: class provides an interface to determine a MIME type
+ * for various content, if it provided with different levels of information.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Magic
+{
+    /**
+     * Returns a copy of the MIME extension map.
+     *
+     * @return array  The MIME extension map.
+     */
+    static protected function _getMimeExtensionMap()
+    {
+        static $mime_extension_map;
+
+        if (!isset($mime_extension_map)) {
+            require dirname(__FILE__) . '/mime.mapping.php';
+        }
+
+        return $mime_extension_map;
+    }
+
+    /**
+     * Returns a copy of the MIME magic file.
+     *
+     * @return array  The MIME magic file.
+     */
+    static protected function _getMimeMagicFile()
+    {
+        static $mime_magic;
+
+        if (!isset($mime_magic)) {
+            require dirname(__FILE__) . '/mime.magic.php';
+        }
+
+        return $mime_magic;
+    }
+
+    /**
+     * Attempt to convert a file extension to a MIME type, based
+     * on the global Horde and application specific config files.
+     *
+     * If we cannot map the file extension to a specific type, then
+     * we fall back to a custom MIME handler 'x-extension/$ext', which
+     * can be used as a normal MIME type internally throughout Horde.
+     *
+     * @param string $ext  The file extension to be mapped to a MIME type.
+     *
+     * @return string  The MIME type of the file extension.
+     */
+    static public function extToMIME($ext)
+    {
+        if (empty($ext)) {
+           return 'application/octet-stream';
+        }
+
+        $ext = String::lower($ext);
+        $map = self::_getMimeExtensionMap();
+        $pos = 0;
+
+        while (!isset($map[$ext])) {
+            if (($pos = strpos($ext, '.')) === false) {
+                break;
+            }
+            $ext = substr($ext, $pos + 1);
+        }
+
+        return isset($map[$ext])
+            ? $map[$ext]
+            : 'x-extension/' . $ext;
+    }
+
+    /**
+     * Attempt to convert a filename to a MIME type, based on the global Horde
+     * and application specific config files.
+     *
+     * @param string $filename  The filename to be mapped to a MIME type.
+     * @param boolean $unknown  How should unknown extensions be handled? If
+     *                          true, will return 'x-extension/*' types.  If
+     *                          false, will return 'application/octet-stream'.
+     *
+     * @return string  The MIME type of the filename.
+     */
+    static public function filenameToMIME($filename, $unknown = true)
+    {
+        $pos = strlen($filename) + 1;
+        $type = '';
+
+        $map = self::_getMimeExtensionMap();
+        for ($i = 0; $i <= $map['__MAXPERIOD__']; ++$i) {
+            $pos = strrpos(substr($filename, 0, $pos - 1), '.') + 1;
+            if ($pos === false) {
+                break;
+            }
+        }
+        $type = self::extToMIME(substr($filename, $pos));
+
+        return (empty($type) || (!$unknown && (strpos($type, 'x-extension') !== false)))
+            ? 'application/octet-stream'
+            : $type;
+    }
+
+    /**
+     * Attempt to convert a MIME type to a file extension, based
+     * on the global Horde and application specific config files.
+     *
+     * If we cannot map the type to a file extension, we return false.
+     *
+     * @param string $type  The MIME type to be mapped to a file extension.
+     *
+     * @return string  The file extension of the MIME type.
+     */
+    static public function MIMEToExt($type)
+    {
+        if (empty($type)) {
+            return false;
+        }
+
+        if (($key = array_search($type, self::_getMimeExtensionMap())) === false) {
+            list($major, $minor) = explode('/', $type);
+            if ($major == 'x-extension') {
+                return $minor;
+            }
+            if (strpos($minor, 'x-') === 0) {
+                return substr($minor, 2);
+            }
+            return false;
+        }
+
+        return $key;
+    }
+
+    /**
+     * Uses variants of the UNIX "file" command to attempt to determine the
+     * MIME type of an unknown file.
+     *
+     * @param string $path      The path to the file to analyze.
+     * @param string $magic_db  Path to the mime magic database.
+     *
+     * @return string  The MIME type of the file.  Returns false if the file
+     *                 type isn't recognized or an error happened.
+     */
+    static public function analyzeFile($path, $magic_db = null)
+    {
+        /* If the PHP Mimetype extension is available, use that. */
+        if (Util::extensionExists('fileinfo')) {
+            $res = empty($magic_db)
+                ? @finfo_open(FILEINFO_MIME)
+                : @finfo_open(FILEINFO_MIME, $magic_db);
+
+            if ($res) {
+                $type = finfo_file($res, $path);
+                finfo_close($res);
+
+                /* Remove any additional information. */
+                foreach (array(';', ',', '\\0') as $separator) {
+                    if (($pos = strpos($type, $separator)) !== false) {
+                        $type = rtrim(substr($type, 0, $pos));
+                    }
+                }
+
+                if (preg_match('|^[a-z0-9]+/[.-a-z0-9]+$|i', $type)) {
+                    return $type;
+                }
+            }
+        }
+
+        if (Util::extensionExists('mime_magic')) {
+            return trim(mime_content_type($path));
+        }
+
+        /* Use a built-in magic file. */
+        $mime_magic = self::_getMimeMagicFile();
+        if (!($fp = @fopen($path, 'rb'))) {
+            return false;
+        }
+
+        reset($mime_magic);
+        while (list($offset, $odata) = each($mime_magic)) {
+            reset($odata);
+            while (list($length, $ldata) = each($odata)) {
+                @fseek($fp, $offset, SEEK_SET);
+                $lookup = @fread($fp, $length);
+                if (!empty($ldata[$lookup])) {
+                    fclose($fp);
+                    return $ldata[$lookup];
+                }
+            }
+        }
+        fclose($fp);
+
+        return false;
+    }
+
+    /**
+     * Uses variants of the UNIX "file" command to attempt to determine the
+     * MIME type of an unknown byte stream.
+     *
+     * @param string $data      The file data to analyze.
+     * @param string $magic_db  Path to the mime magic database.
+     *
+     * @return string  The MIME type of the file.  Returns false if the file
+     *                 type isn't recognized or an error happened.
+     */
+    static public function analyzeData($data, $magic_db = null)
+    {
+        /* If the PHP Mimetype extension is available, use that. */
+        if (Util::extensionExists('fileinfo')) {
+            $res = empty($magic_db)
+                ? @finfo_open(FILEINFO_MIME)
+                : @finfo_open(FILEINFO_MIME, $magic_db);
+
+            if (!$res) {
+                return false;
+            }
+
+            $type = finfo_buffer($res, $data);
+            finfo_close($res);
+
+            /* Remove any additional information. */
+            if (($pos = strpos($type, ';')) !== false) {
+                $type = rtrim(substr($type, 0, $pos));
+            }
+
+            if (($pos = strpos($type, ',')) !== false) {
+                $type = rtrim(substr($type, 0, $pos));
+            }
+
+            return $type;
+        }
+
+        /* Use a built-in magic file. */
+        $mime_magic = self::_getMimeMagicFile();
+        reset($mime_magic);
+        while (list($offset, $odate) = each($mime_magic)) {
+            reset($odata);
+            while (list($length, $ldata) = each($odata)) {
+                $lookup = substr($data, $offset, $length);
+                if (!empty($ldata[$lookup])) {
+                    return $ldata[$lookup];
+                }
+            }
+        }
+
+        return false;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Mail.php b/framework/mime_tmp/Horde/Mime/Mail.php
new file mode 100644 (file)
index 0000000..289fc9a
--- /dev/null
@@ -0,0 +1,507 @@
+<?php
+
+require_once 'Horde/String.php';
+require_once dirname(__FILE__) . '/../MIME.php';
+require_once dirname(__FILE__) . '/Headers.php';
+require_once dirname(__FILE__) . '/Message.php';
+require_once 'Mail.php';
+
+/**
+ * The Horde_MIME_Mail:: class wraps around the various MIME library classes
+ * to provide a simple interface for creating and sending MIME messages.
+ *
+ * Copyright 2007-2008 The Horde Project (http://www.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@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Mail
+{
+    /**
+     * The message headers.
+     *
+     * @var Horde_MIME_Headers
+     */
+    protected $_headers;
+
+    /**
+     * The main body part.
+     *
+     * @var Horde_MIME_Part
+     */
+    protected $_body;
+
+    /**
+     * The main HTML body part.
+     *
+     * @var Horde_MIME_Part
+     */
+    protected $_htmlBody;
+
+    /**
+     * The message recipients.
+     *
+     * @var array
+     */
+    protected $_recipients = array();
+
+    /**
+     * All MIME parts except the main body part.
+     *
+     * @var array
+     */
+    protected $_parts = array();
+
+    /**
+     * The Mail driver name.
+     *
+     * @link http://pear.php.net/Mail
+     * @var string
+     */
+    protected $_mailer_driver = 'smtp';
+
+    /**
+     * The Mail driver parameters.
+     *
+     * @link http://pear.php.net/Mail
+     * @var array
+     */
+    protected $_mailer_params = array();
+
+    /**
+     * Constructor.
+     *
+     * @param string $subject  The message subject.
+     * @param string $body     The message body.
+     * @param string $to       The message recipient(s).
+     * @param string $from     The message sender.
+     * @param string $charset  The character set of the message.
+     */
+    function __construct($subject = null, $body = null, $to = null,
+                         $from = null, $charset = 'iso-8859-1')
+    {
+        /* Set SERVER_NAME. */
+        if (!isset($_SERVER['SERVER_NAME'])) {
+            $_SERVER['SERVER_NAME'] = php_uname('n');
+        }
+
+        $this->_headers = new Horde_MIME_Headers();
+
+        if ($subject) {
+            $this->addHeader('Subject', $subject, $charset);
+        }
+        if ($to) {
+            $this->addHeader('To', $to, $charset);
+        }
+        if ($from) {
+            $this->addHeader('From', $from, $charset);
+        }
+        if ($body) {
+            $this->setBody($body, $charset);
+        }
+    }
+
+    /**
+     * Adds several message headers at once.
+     *
+     * @param array $header    Hash with header names as keys and header
+     *                         contents as values.
+     * @param string $charset  The header value's charset.
+     */
+    public function addHeaders($headers = array(), $charset = 'iso-8859-1')
+    {
+        foreach ($headers as $header => $value) {
+            if (is_a($added = $this->addHeader($header, $value, $charset), 'PEAR_Error')) {
+                return $added;
+            }
+        }
+    }
+
+    /**
+     * Adds a message header.
+     *
+     * @param string $header      The header name.
+     * @param string $value       The header value.
+     * @param string $charset     The header value's charset.
+     * @param boolean $overwrite  If true, an existing header of the same name
+     *                            is being overwritten; if false, multiple
+     *                            headers are added; if null, the correct
+     *                            behaviour is automatically chosen depending
+     *                            on the header name.
+     */
+    public function addHeader($header, $value, $charset = 'iso-8859-1',
+                              $overwrite = null)
+    {
+        $lc_header = String::lower($header);
+
+        /* Only encode value if charset is explicitly specified, otherwise
+         * the message's charset will be used when building the message. */
+        if (!empty($charset)) {
+            if (in_array($lc_header, $this->_headers->addressFields())) {
+                $value = Horde_MIME::encodeAddress($value, $charset);
+            } else {
+                $value = Horde_MIME::encode($value, $charset);
+            }
+        }
+
+        if (is_null($overwrite)) {
+            if (in_array($lc_header, $this->_headers->singleFields(true))) {
+                $overwrite = true;
+            }
+        }
+
+        if ($overwrite) {
+            $this->_headers->removeHeader($header);
+        }
+
+        if ($lc_header !== 'bcc') {
+            $this->_headers->addHeader($header, $value);
+        }
+
+        if (in_array($lc_header, array('to', 'cc', 'bcc'))) {
+            return $this->addRecipients($value);
+        }
+    }
+
+    /**
+     * Removes a message header.
+     *
+     * @param string $header  The header name.
+     */
+    public function removeHeader($header)
+    {
+        $value = $this->_headers->getValue($header);
+        $this->_headers->removeHeader($header);
+        if (in_array(String::lower($header), array('to', 'cc', 'bcc'))) {
+            $this->removeRecipients($value);
+        }
+    }
+
+    /**
+     * Sets the message body text.
+     *
+     * @param string $body             The message content.
+     * @param string $charset          The character set of the message.
+     * @param boolean|integer $wrap    If true, wrap the message at column 76;
+     *                                 If an integer wrap the message at that
+     *                                 column. Don't use wrapping if sending
+     *                                 flowed messages.
+     */
+    public function setBody($body, $charset = 'iso-8859-1', $wrap = false)
+    {
+        if ($wrap) {
+            $body = String::wrap($body, $wrap === true ? 76 : $wrap, "\n");
+        }
+        $this->_body = new Horde_MIME_Part('text/plain', $body, $charset);
+    }
+
+    /**
+     * Sets the HTML message body text.
+     *
+     * @param string $body          The message content.
+     * @param string $charset       The character set of the message.
+     * @param boolean $alternative  If true, a multipart/alternative message is
+     *                              created and the text/plain part is
+     *                              generated automatically. If false, a
+     *                              text/html message is generated.
+     */
+    public function setHTMLBody($body, $charset = 'iso-8859-1',
+                                $alternative = true)
+    {
+        $this->_htmlBody = new Horde_MIME_Part('text/html', $body, $charset);
+        if ($alternative) {
+            require_once 'Horde/Text/Filter.php';
+            $body = Text_Filter::filter($body, 'html2text', array('wrap' => false));
+            $this->_body = new Horde_MIME_Part('text/plain', $body, $charset);
+        }
+    }
+
+    /**
+     * Adds a message part.
+     *
+     * @param string $mime_type    The content type of the part.
+     * @param string $content      The content of the part.
+     * @param string $charset      The character set of the part.
+     * @param string $disposition  The content disposition of the part.
+     *
+     * @return integer  The part number.
+     */
+    public function addPart($mime_type, $content, $charset = 'us-ascii',
+                            $disposition = null)
+    {
+        $part = new Horde_MIME_Part($mime_type, $content, $charset, $disposition);
+        $part->transferEncodeContents();
+        $this->_parts[] = $part;
+        return count($this->_parts) - 1;
+    }
+
+    /**
+     * Adds a MIME message part.
+     *
+     * @param Horde_MIME_Part $part  A Horde_MIME_Part object.
+     *
+     * @return integer  The part number.
+     */
+    public function addMIMEPart($part)
+    {
+        $part->transferEncodeContents();
+        $this->_parts[] = $part;
+        return count($this->_parts) - 1;
+    }
+
+    /**
+     * Adds an attachment.
+     *
+     * @param string $file     The path to the file.
+     * @param string $name     The file name to use for the attachment.
+     * @param string $type     The content type of the file.
+     * @param string $charset  The character set of the part (only relevant for
+     *                         text parts.
+     *
+     * @return integer  The part number.
+     */
+    public function addAttachment($file, $name = null, $type = null,
+                                  $charset = 'us-ascii')
+    {
+        if (empty($name)) {
+            $name = basename($file);
+        }
+        if (empty($type)) {
+            require_once dirname(__FILE__) . '/Magic.php';
+            $type = Horde_MIME_Magic::filenameToMIME($file, false);
+        }
+
+        $part = new Horde_MIME_Part($type, file_get_contents($file), $charset, 'attachment');
+        $part->setName($name);
+        $part->transferEncodeContents();
+        $this->_parts[] = $part;
+
+        return count($this->_parts) - 1;
+    }
+
+    /**
+     * Removes a message part.
+     *
+     * @param integer $part  The part number.
+     */
+    public function removePart($part)
+    {
+        if (isset($this->_parts[$part])) {
+            unset($this->_parts[$part]);
+        }
+    }
+
+    /**
+     * Adds message recipients.
+     *
+     * Recipients specified by To:, Cc:, or Bcc: headers are added
+     * automatically.
+     *
+     * @param string|array  List of recipients, either as a comma separated
+     *                      list or as an array of email addresses.
+     */
+    public function addRecipients($recipients)
+    {
+        $recipients = $this->_buildRecipients($recipients);
+        if (is_a($recipients, 'PEAR_Error')) {
+            return $recipients;
+        }
+        $this->_recipients = array_merge($this->_recipients, $recipients);
+    }
+
+    /**
+     * Removes message recipients.
+     *
+     * @param string|array  List of recipients, either as a comma separated
+     *                      list or as an array of email addresses.
+     */
+    public function removeRecipients($recipients)
+    {
+        $recipients = $this->_buildRecipients($recipients);
+        if (is_a($recipients, 'PEAR_Error')) {
+            return $recipients;
+        }
+        $this->_recipients = array_diff($this->_recipients, $recipients);
+    }
+
+    /**
+     * Removes all message recipients.
+     */
+    public function clearRecipients()
+    {
+        $this->_recipients = array();
+    }
+
+    /**
+     * Builds a recipients list.
+     *
+     * @param string|array  List of recipients, either as a comma separated
+     *                      list or as an array of email addresses.
+     *
+     * @return array  Normalized list of recipients or PEAR_Error on failure.
+     */
+    protected function _buildRecipients($recipients)
+    {
+        if (is_string($recipients)) {
+            $recipients = Horde_MIME::explode($recipients, ',');
+        }
+        $recipients = array_filter(array_map('trim', $recipients));
+
+        $addrlist = array();
+        foreach ($recipients as $email) {
+            if (!empty($email)) {
+                $unique = Horde_MIME::bareAddress($email);
+                if ($unique) {
+                    $addrlist[$unique] = $email;
+                } else {
+                    $addrlist[$email] = $email;
+                }
+            }
+        }
+
+        foreach (Horde_MIME::bareAddress(implode(', ', $addrlist), null, true) as $val) {
+            if (Horde_MIME::is8bit($val)) {
+                return PEAR::raiseError(sprintf(_("Invalid character in e-mail address: %s."), $val));
+            }
+        }
+
+        return $addrlist;
+    }
+
+    /**
+     * Sends this message.
+     *
+     * For the possible Mail drivers and parameters see the PEAR Mail
+     * documentation.
+     * @link http://pear.php.net/Mail
+     *
+     * @param string $driver   The Mail driver to use.
+     * @param array $params    Any parameters necessary for the Mail driver.
+     * @param boolean $resend  If true, the message id and date are re-used;
+     *                         If false, they will be updated.
+     * @param boolean $flowed  Send message in flowed text format. @since
+     *                         Horde 3.2.1
+     *
+     * @return mixed  True on success, PEAR_Error on error.
+     */
+    public function send($driver = null, $params = array(), $resend = false,
+                         $flowed = true)
+    {
+        /* Add mandatory headers if missing. */
+        if (!$resend || !$this->_headers->getString('Message-ID')) {
+            $this->_headers->addMessageIdHeader();
+        }
+        if (!$this->_headers->getString('User-Agent')) {
+            $this->_headers->addAgentHeader();
+        }
+        if (!$resend || !$this->_headers->getString('Date')) {
+            $this->_headers->addHeader('Date', date('r'));
+        }
+
+        /* Send in flowed format. */
+        if ($flowed && !empty($this->_body)) {
+            require_once 'Text/Flowed.php';
+            $flowed = new Text_Flowed($this->_body->getContents(),
+                                      $this->_body->getCharset());
+            $flowed->setDelSp(true);
+            $this->_body->setContentTypeParameter('DelSp', 'Yes');
+            $this->_body->setContents($flowed->toFlowed());
+            $this->_body->setContentTypeParameter('format', 'flowed');
+        }
+
+        /* Build mime message. */
+        $mime = new Horde_MIME_Message();
+        if (!empty($this->_body) && !empty($this->_htmlBody)) {
+            $basepart = new Horde_MIME_Part('multipart/alternative');
+            $this->_body->setDescription(_("Plaintext Version of Message"));
+            $basepart->addPart($this->_body);
+            $this->_htmlBody->setDescription(_("HTML Version of Message"));
+            $basepart->addPart($this->_htmlBody);
+            $mime->addPart($basepart);
+        } elseif (!empty($this->_htmlBody)) {
+            $mime->addPart($this->_htmlBody);
+        } elseif (!empty($this->_body)) {
+            $mime->addPart($this->_body);
+        }
+        foreach ($this->_parts as $mime_part) {
+            $mime->addPart($mime_part);
+        }
+
+        /* Check mailer configuration. */
+        if (!empty($driver)) {
+            $this->_mailer_driver = $driver;
+        }
+        if (!empty($params)) {
+            $this->_mailer_params = $params;
+        }
+
+        /* Send message. */
+        return $mime->send(implode(', ', $this->_recipients), $this->_headers,
+                           $this->_mailer_driver, $this->_mailer_params);
+    }
+
+    /**
+     * Return error string corresponding to a sendmail error code.
+     *
+     * @param integer $code  The error code.
+     *
+     * @return string  The error string, or null if the code is unknown.
+     */
+    static public function sendmailError($code)
+    {
+        switch ($code) {
+        case 64: // EX_USAGE
+            return 'sendmail: ' . _("command line usage error") . ' (64)';
+
+        case 65: // EX_DATAERR
+            return 'sendmail: ' . _("data format error") . ' (65)';
+
+        case 66: // EX_NOINPUT
+            return 'sendmail: ' . _("cannot open input") . ' (66)';
+
+        case 67: // EX_NOUSER
+            return 'sendmail: ' . _("addressee unknown") . ' (67)';
+
+        case 68: // EX_NOHOST
+            return 'sendmail: ' . _("host name unknown") . ' (68)';
+
+        case 69: // EX_UNAVAILABLE
+            return 'sendmail: ' . _("service unavailable") . ' (69)';
+
+        case 70: // EX_SOFTWARE
+            return 'sendmail: ' . _("internal software error") . ' (70)';
+
+        case 71: // EX_OSERR
+            return 'sendmail: ' . _("system error") . ' (71)';
+
+        case 72: // EX_OSFILE
+            return 'sendmail: ' . _("critical system file missing") . ' (72)';
+
+        case 73: // EX_CANTCREAT
+            return 'sendmail: ' . _("cannot create output file") . ' (73)';
+
+        case 74: // EX_IOERR
+            return 'sendmail: ' . _("input/output error") . ' (74)';
+
+        case 75: // EX_TEMPFAIL
+            return 'sendmail: ' . _("temporary failure") . ' (75)';
+
+        case 76: // EX_PROTOCOL
+            return 'sendmail: ' . _("remote error in protocol") . ' (76)';
+
+        case 77: // EX_NOPERM
+            return 'sendmail: ' . _("permission denied") . ' (77)';
+
+        case 78: // EX_CONFIG
+            return 'sendmail: ' . _("configuration error") . ' (78)';
+
+        case 79: // EX_NOTFOUND
+            return 'sendmail: ' . _("entry not found") . ' (79)';
+        }
+
+        return null;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Mdn.php b/framework/mime_tmp/Horde/Mime/Mdn.php
new file mode 100644 (file)
index 0000000..d567c88
--- /dev/null
@@ -0,0 +1,238 @@
+<?php
+/**
+ * The Horde_MIME_MDN:: class implements Message Disposition Notifications as
+ * described by RFC 3798.
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_MDN
+{
+    /**
+     * The Horde_MIME_Headers object.
+     *
+     * @var Horde_MIME_Headers
+     */
+    protected $_headers;
+
+    /**
+     * The text of the original message.
+     *
+     * @var string
+     */
+    protected $_msgtext = false;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_MIME_Headers $mime_headers  A Horde_MIME_Headers object.
+     */
+    function __construct($headers = null)
+    {
+        $this->_headers = $headers;
+    }
+
+    /**
+     * Returns the address to return the MDN to.
+     *
+     * @return string  The address to send the MDN to. Returns null if no
+     *                 MDN is requested.
+     */
+    public function getMDNReturnAddr()
+    {
+        /* RFC 3798 [2.1] requires the Disposition-Notificaion-To header
+         * for an MDN to be created. */
+        return $this->_headers->getValue('Disposition-Notification-To');
+    }
+
+    /**
+     * Is user input required to send the MDN?
+     * Explicit confirmation is needed in some cases to prevent mail loops
+     * and the use of MDNs for mail bombing.
+     *
+     * @return boolean  Is explicit user input required to send the MDN?
+     */
+    public function userConfirmationNeeded()
+    {
+        $return_path = $this->_headers->getValue('Return-Path');
+
+        /* RFC 3798 [2.1]: Explicit confirmation is needed if there is no
+         * Return-Path in the header. Also, "if the message contains more
+         * than one Return-Path header, the implementation may [] treat the
+         * situation as a failure of the comparison." */
+        if (empty($return_path) || is_array($return_path)) {
+            return true;
+        }
+
+        require_once dirname(__FILE__) . '/Address.php';
+
+        /* RFC 3798 [2.1]: Explicit confirmation is needed if there is more
+         * than one distinct address in the Disposition-Notification-To
+         * header. */
+        $addr_arr = Horde_MIME_Address::parseAddressList($this->getMDNReturnAddr());
+        if (count($addr_arr) > 1) {
+            return true;
+        }
+
+        /* RFC 3798 [2.1] states that "MDNs SHOULD NOT be sent automatically
+         * if the address in the Disposition-Notification-To header differs
+         * from the address in the Return-Path header." This comparison is
+         * case-sensitive for the mailbox part and case-insensitive for the
+         * host part. */
+        $ret_arr = Horde_MIME_Address::parseAddressList($return_path);
+        return ($addr_arr[0]['mailbox'] == $ret_arr[0]['mailbox']) &&
+               (String::lower($addr_arr[0]['host']) == String::lower($ret_arr[0]['host']));
+    }
+
+    /**
+     * When generating the MDN, should we return the enitre text of the
+     * original message?  The default is no - we only return the headers of
+     * the original message. If the text is passed in via this method, we
+     * will return the entire message.
+     *
+     * @param string $text  The text of the original message.
+     */
+    public function originalMessageText($text)
+    {
+        $this->_msgtext = $text;
+    }
+
+    /**
+     * Generate the MDN according to the specifications listed in RFC
+     * 3798 [3].
+     *
+     * @param boolean $action   Was this MDN type a result of a manual action
+     *                          on part of the user?
+     * @param boolean $sending  Was this MDN sent as a result of a manual
+     *                          action on part of the user?
+     * @param string $type      The type of action performed by the user.
+     * <pre>
+     * Per RFC 3798 [3.2.6.2] the following types are valid:
+     * =====================================================
+     * 'displayed'
+     * 'deleted'
+     * </pre>
+     * @param array $mod        The list of modifications.
+     * <pre>
+     * Per RFC 3798 [3.2.6.3] the following modifications are valid:
+     * =============================================================
+     * 'error'
+     * </pre>
+     * @param array $err        If $mod is 'error', the additional information
+     *                          to provide.  Key is the type of modification,
+     *                          value is the text.
+     *
+     * @return mixed  True on success, PEAR_Error object on error.
+     */
+    public function generate($action, $sending, $type, $mod = array(),
+                             $err = array())
+    {
+        require_once dirname(__FILE__) . '/Headers.php';
+        require_once dirname(__FILE__) . '/Message.php';
+        require_once 'Horde/Identity.php';
+        require_once 'Horde/Text.php';
+
+        /* Set up some variables we use later. */
+        $identity = &Identity::singleton();
+        $from_addr = $identity->getDefaultFromAddress();
+
+        $to = $this->getMDNReturnAddr();
+        $ua = $this->_headers->getAgentHeader();
+
+        $orig_recip = $this->_headers->getValue('Original-Recipient');
+        if (!empty($orig_recip) && is_array($orig_recip)) {
+            $orig_recip = $orig_recip[0];
+        }
+
+        $msg_id = $this->_headers->getValue('Message-ID');
+
+        /* Create the Disposition field now (RFC 3798 [3.2.6]). */
+        $dispo = 'Disposition: ' .
+                 (($action) ? 'manual-action' : 'automatic-action') .
+                 '/' .
+                 (($sending) ? 'MDN-sent-manually' : 'MDN-sent-automatically') .
+                 '; ' .
+                 $type;
+        if (!empty($mod)) {
+            $dispo .= '/' . implode(', ', $mod);
+        }
+
+        /* Set up the mail headers. */
+        $msg_headers = new Horde_MIME_Headers();
+        $msg_headers->addMessageIdHeader();
+        $msg_headers->addAgentHeader($ua);
+        $msg_headers->addHeader('Date', date('r'));
+        $msg_headers->addHeader('From', $from_addr);
+        $msg_headers->addHeader('To', $this->getMDNReturnAddr());
+        $msg_headers->addHeader('Subject', _("Disposition Notification"));
+
+        /* MDNs are a subtype of 'multipart/report'. */
+        $msg = new Horde_MIME_Message();
+        $msg->setType('multipart/report');
+        $msg->setContentTypeParameter('report-type', 'disposition-notification');
+
+        $charset = NLS::getCharset();
+
+        /* The first part is a human readable message. */
+        $part_one = new Horde_MIME_Part('text/plain');
+        $part_one->setCharset($charset);
+        if ($type == 'displayed') {
+            $contents = sprintf(_("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers->getValue('Date'), $this->_headers->getValue('To'), $this->_headers->getValue('Subject'));
+            require_once 'Text/Flowed.php';
+            $flowed = new Text_Flowed($contents, $charset);
+            $flowed->setDelSp(true);
+            $part_one->setContentTypeParameter('format', 'flowed');
+            $part_one->setContentTypeParameter('DelSp', 'Yes');
+            $part_one->setContents($flowed->toFlowed());
+        }
+        // TODO: Messages for other notification types.
+        $msg->addPart($part_one);
+
+        /* The second part is a machine-parseable description. */
+        $part_two = new Horde_MIME_Part('message/disposition-notification');
+        $part_two->setContents('Reporting-UA: ' . $GLOBALS['conf']['server']['name'] . '; ' . $ua . "\n");
+        if (!empty($orig_recip)) {
+            $part_two->appendContents('Original-Recipient: rfc822;' . $orig_recip . "\n");
+        }
+        $part_two->appendContents('Final-Recipient: rfc822;' . $from_addr . "\n");
+        if (!empty($msg_id)) {
+            $part_two->appendContents('Original-Message-ID: rfc822;' . $msg_id . "\n");
+        }
+        $part_two->appendContents($dispo . "\n");
+        if (in_array('error', $mod) && isset($err['error'])) {
+            $part_two->appendContents('Error: ' . $err['error'] . "\n");
+        }
+        $msg->addPart($part_two);
+
+        /* The third part is the text of the original message.  RFC 3798 [3]
+         * allows us to return only a portion of the entire message - this
+         * is left up to the user. */
+        $part_three = new Horde_MIME_Part('message/rfc822');
+        $part_three->setContents($this->_headers->toString());
+        if (!empty($this->_msgtext)) {
+            $part_three->appendContents($part_three->getEOL() . $this->_msgtext);
+        }
+        $msg->addPart($part_three);
+
+        return $msg->send($to, $msg_headers);
+    }
+
+    /**
+     * Add a MDN (read receipt) request headers to the Horde_MIME_Headers::
+     * object.
+     *
+     * @param Horde_MIME_Headers &$ob  The object to add the headers to.
+     * @param string $to               The address the receipt should be
+     *                                 mailed to.
+     */
+    public function addMDNRequestHeaders(&$ob, $to)
+    {
+        /* This is the RFC 3798 way of requesting a receipt. */
+        $ob->addHeader('Disposition-Notification-To', $to);
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Message.php b/framework/mime_tmp/Horde/Mime/Message.php
new file mode 100644 (file)
index 0000000..6844080
--- /dev/null
@@ -0,0 +1,384 @@
+<?php
+
+require_once dirname(__FILE__) . '/Part.php';
+
+/**
+ * The Horde_MIME_Message:: class provides methods for creating and
+ * manipulating MIME email messages.
+ *
+ * $Horde: framework/MIME/MIME/Message.php,v 1.107 2008/10/17 05:41:59 slusarz Exp $
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@horde.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Message extends Horde_MIME_Part
+{
+    /**
+     * Create a Horde_MIME_Message object from a Horde_MIME_Part object.
+     * This function can be called statically via:
+     *    $mime_message = Horde_MIME_Message::convertMIMEPart();
+     *
+     * @todo Is this needed?
+     *
+     * @param Horde_MIME_Part $mime_part  The Horde_MIME_Part object.
+     *
+     * @return Horde_MIME_Message  The new Horde_MIME_Message object.
+     */
+    static public function convertMIMEPart($mime_part)
+    {
+        if (!$mime_part->getMIMEId()) {
+            $mime_part->setMIMEId(1);
+        }
+
+        $mime_message = new Horde_MIME_Message();
+        $mime_message->addPart($mime_part);
+
+        return $mime_message;
+    }
+
+    /**
+     * Sends this message.
+     *
+     * @param string $email                 The address list to send to.
+     * @param Horde_MIME_Headers $headers   The Horde_MIME_Headers object
+     *                                      holding this message's headers.
+     * @param string $driver                The Mail:: driver to use.
+     * @param array $params                 Any parameters necessary for the
+     *                                      Mail driver.
+     *
+     * @return mixed  True on success, PEAR_Error on error.
+     */
+    public function send($email, $headers, $driver = null, $params = array())
+    {
+        if (!isset($driver)) {
+            $driver = $GLOBALS['conf']['mailer']['type'];
+            $params = $GLOBALS['conf']['mailer']['params'];
+        }
+
+        require_once 'Mail.php';
+        $mailer = Mail::factory($driver, $params);
+
+        /* Add MIME Headers if they don't already exist. */
+        if (!$headers->getValue('MIME-Version')) {
+            $headers = $this->addMIMEHeaders($headers);
+        }
+        $headerArray = $headers->toArray($this->getCharset());
+
+        /* Does the SMTP backend support 8BITMIME (RFC 1652) or
+         * BINARYMIME (RFC 3030) extensions? Requires PEAR's Mail package
+         * version 1.2+ and Net_SMTP version 1.3+. */
+        if (($driver == 'smtp') && method_exists($mailer, 'getSMTPObject')) {
+            $net_smtp = $mailer->getSMTPObject();
+            if (!is_a($net_smtp, 'PEAR_Error') &&
+                method_exists($net_smtp, 'getServiceExtensions')) {
+                $smtp_ext = $net_smtp->getServiceExtensions();
+                $message->strict7bit(false);
+                $encoding = $message->getTransferEncoding();
+                if (($encoding == '8bit') &&
+                    isset($smtp_ext['8BITMIME'])) {
+                    $mailer->addServiceExtensionParameter('BODY', '8BITMIME');
+                } elseif (($encoding == 'binary') &&
+                          isset($smtp_ext['BINARYMIME'])) {
+                    $mailer->addServiceExtensionParameter('BODY', 'BINARYMIME');
+                } else {
+                    $message->strict7bit(true);
+                    $encoding = $message->getTransferEncoding();
+                }
+                $headers->addHeader('Content-Transfer-Encoding', $encoding);
+            }
+        }
+
+        /* Make sure the message has a trailing newline. */
+        $msg = $this->toString();
+        if (substr($msg, -1) != "\n") {
+            $msg .= "\n";
+        }
+
+        $result = $mailer->send(Horde_MIME::encodeAddress($email), $headerArray, $msg);
+
+        if (is_a($result, 'PEAR_Error') && ($driver == 'sendmail')) {
+            $error = Horde_MIME_Mail::sendmailError($result->getCode());
+            if (is_null($error)) {
+                $error = $result;
+                $userinfo = null;
+            } else {
+                $userinfo = $result->toString();
+            }
+            return PEAR::raiseError($error, null, null, null, $userinfo);
+        }
+
+        return $result;
+    }
+
+    /**
+     * Get the list of MIME headers for this part in an array.
+     *
+     * @return array  The full set of MIME headers.
+     */
+    public function getHeaderArray()
+    {
+        /* Per RFC 2045 [4], this MUST appear in the message headers. */
+        return parent::header(array('MIME-Version' => '1.0'));
+    }
+
+    /**
+     * Parse an array of MIME structure information into a Horde_MIME_Message
+     * object.
+     * This function can be called statically via:
+     *    $mime_message = Horde_MIME_Message::parseStructure();
+     *
+     * @param array $structure  An array of structure information in the
+     *                          following format:
+     * <pre>
+     * MANDATORY:
+     *   'type' - (string) The MIME type
+     *   'subtype' - (string) The MIME subtype
+     *
+     * The array MAY contain the following information:
+     *   'disposition' - (string) The disposition type of the part (e.g.
+     *                   'attachment', 'inline').
+     *   'dparameters' - (array) Attribute/value pairs from the part's
+     *                   Content-Disposition header.
+     *   'language' - (array) A list of body language values.
+     *   'location' - (string) The body content URI.
+     *
+     * Depending on the MIME type of the part, the array will also contain
+     * further information. If labeled as [OPTIONAL], the array MAY
+     * contain this information, but only if 'noext' is false and the
+     * server returned the requested information. Else, the value is not
+     * set.
+     *
+     * multipart/* parts:
+     * ==================
+     * 'parts' - (array) An array of subparts (follows the same format as
+     *           the base structure array).
+     * 'parameters' - [OPTIONAL] (array) Attribute/value pairs from the
+     *                part's Content-Type header.
+     *
+     * All other parts:
+     * ================
+     * 'parameters' - (array) Attribute/value pairs from the part's
+     *                Content-Type header.
+     * 'id' - (string) The part's Content-ID value.
+     * 'description' - (string) The part's Content-Description value.
+     * 'encoding' - (string) The part's Content-Transfer-Encoding value.
+     * 'size' - (integer) - The part's size in bytes.
+     * 'envelope' - [ONLY message/rfc822] (array) See 'envelope' response.
+     * 'structure' - [ONLY message/rfc822] (array) See 'structure'
+     *               response.
+     * 'lines' - [ONLY message/rfc822 and text/*] (integer) The size of
+     *           the body in text lines.
+     * 'md5' - [OPTIONAL] (string) The part's MD5 value.
+     * </pre>
+     *
+     * @return object  A Horde_MIME_Message object.
+     */
+    static public function parseStructure($structure)
+    {
+        $ob = self::_parseStructure($structure, true);
+        $ob->buildMIMEIds();
+        return $ob;
+    }
+
+    /**
+     * Parse a subpart of a MIME message into a
+     * Horde_MIME_Message/Horde_MIME_Part object.
+     *
+     * @param array $data      Structure information in the format described
+     *                         in parseStructure().
+     * @param boolean $rfc822  Force the part to be treated as a
+     *                         message/rfc822 part.
+     *
+     * @return mixed  Returns either a Horde_MIME_Message or a Horde_MIME_Part
+     *                object, depending on the part's MIME type.
+     */
+    static protected function _parseStructure($data, $rfc822 = false)
+    {
+        $type = $data['type'] . '/' . $data['subtype'];
+
+        if ($rfc822 || ($type == 'message/rfc822')) {
+            $ob = new Horde_MIME_Message();
+        } else {
+            $ob = new Horde_MIME_Part();
+        }
+
+        $ob->setType($type);
+
+        if (isset($data['encoding'])) {
+            $ob->setTransferEncoding($data['encoding']);
+        }
+
+        if (isset($data['disposition'])) {
+            $ob->setDisposition($data['disposition']);
+            if (!empty($data['dparameters'])) {
+                foreach ($data['dparameters'] as $key => $val) {
+                    $ob->setDispositionParameter($key, $val);
+                }
+            }
+        }
+
+        if (isset($data['size'])) {
+            $ob->setBytes($data['size']);
+        }
+
+        if (isset($data['id'])) {
+            $ob->setContentID($data['id']);
+        }
+
+        if (!empty($data['parameters'])) {
+            foreach ($data['parameters'] as $key => $val) {
+                $ob->setContentTypeParameter($key, $val);
+            }
+        }
+
+        /* Set the default character set. */
+        if (($data['subtype'] == 'text') &&
+            (String::lower($ob->getCharset()) == 'us-ascii') &&
+            isset($GLOBALS['mime_structure']['default_charset'])) {
+            /* @todo - switch to using static variable for this. */
+            //$ob->setCharset($GLOBALS['mime_structure']['default_charset']);
+        }
+
+        if (isset($data['description'])) {
+            $ob->setDescription($data['description']);
+        }
+
+        /* Set the name. */
+        if (!$ob->getName()) {
+            $ob->setName($ob->getDispositionParameter('filename'));
+        }
+
+        // @todo Handle language, location, md5, lines, envelope
+
+        /* Add multipart parts. */
+        if (!empty($data['parts'])) {
+            reset($data['parts']);
+            while (list(,$val) = each($data['parts'])) {
+                $ob->addPart(self::_parseStructure($val));
+            }
+        } elseif (!empty($data['structure'])) {
+            $ob->addPart(self::_parseStructure($data['structure']));
+        }
+
+        return $ob;
+    }
+
+    /**
+     * Attempts to build a Horde_MIME_Message object from message text.
+     * This function can be called statically via:
+     *    $mime_message = Horde_MIME_Message::parseMessage();
+     *
+     * @param string $text  The text of the MIME message.
+     *
+     * @return Horde_MIME_Message  A Horde_MIME_Message object, or false on
+     *                             error.
+     */
+    static public function parseMessage($text)
+    {
+        /* Set up the options for the mimeDecode class. */
+        $decode_args = array(
+            'include_bodies' => true,
+            'decode_bodies' => false,
+            'decode_headers' => false
+        );
+
+        require_once 'Mail/mimeDecode.php';
+        $mimeDecode = new Mail_mimeDecode($text, Horde_MIME_Part::EOL);
+        if (!($ob = $mimeDecode->decode($decode_args))) {
+            return false;
+        }
+
+        return self::parseStructure(self::_convertMimeDecodeData($ob));
+    }
+
+    /**
+     * Convert the output from Mail_mimeDecode::decode() into a structure that
+     * parse() can handle.
+     *
+     * @param stdClass $ob  The output from Mail_mimeDecode::decode().
+     *
+     * @return array  An array of structure information.
+     */
+    static protected function _convertMimeDecodeData($ob)
+    {
+        /* Primary content-type. */
+        if (isset($ob->ctype_primary)) {
+            $part = array(
+                'type' => strtolower($ob->ctype_primary),
+                'subtype' => isset($ob->ctype_secondary) ? strtolower($ob->ctype_secondary) : 'x-unknown'
+            );
+        } else {
+            $part = array(
+                'type' => 'application',
+                'subtype' => 'octet-stream'
+            );
+        }
+
+        /* Content transfer encoding. */
+        if (isset($ob->headers['content-transfer-encoding'])) {
+            $part['encoding'] = strtolower($ob->headers['content-transfer-encoding']);
+        }
+
+        /* Content-type and Disposition parameters. */
+        $param_types = array(
+            'ctype_parameters' => 'parameters',
+            'd_parameters' => 'dparameters'
+        );
+
+        foreach ($param_types as $param_key => $param_value) {
+            if (isset($ob->$param_key)) {
+                $part[$param_value] = array();
+                foreach ($ob->$param_key as $key => $val) {
+                    $part[$param_value][strtolower($key)] = $val;
+                }
+            }
+        }
+
+        /* Content-Description. */
+        if (isset($ob->headers['content-description'])) {
+            $part['description'] = $ob->headers['content-description'];
+        }
+
+        /* Content-Disposition. */
+        if (isset($ob->headers['content-disposition'])) {
+            $hdr = $ob->headers['content-disposition'];
+            $pos = strpos($hdr, ';');
+            if ($pos !== false) {
+                $hdr = substr($hdr, 0, $pos);
+            }
+            $part['disposition'] = strtolower($hdr);
+        }
+
+        /* Content-ID. */
+        if (isset($ob->headers['content-id'])) {
+            $part['id'] = $ob->headers['content-id'];
+        }
+
+        /* Get file size (if 'body' text is set). */
+        if (isset($ob->body) &&
+            ($part['type'] != 'message') &&
+            ($part['subtype'] != 'rfc822')) {
+            /* Mail_mimeDecode puts an extra linebreak at the end of body
+             * text. */
+            $size = strlen(str_replace(array("\r\n", "\n"), array("\n", "\r\n"), $ob->body)) - 2;
+            $part['size'] = ($size < 0) ? 0 : $size;
+        }
+
+        /* Process parts also. */
+        if (isset($ob->parts)) {
+            $part['parts'] = array();
+            reset($ob->parts);
+            while (list($key,) = each($ob->parts)) {
+                $part['parts'][] = self::_convertMimeDecodeData($ob->parts[$key]);
+            }
+        }
+
+        return $part;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Part.php b/framework/mime_tmp/Horde/Mime/Part.php
new file mode 100644 (file)
index 0000000..6af6251
--- /dev/null
@@ -0,0 +1,1324 @@
+<?php
+
+require_once 'Horde/String.php';
+require_once dirname(__FILE__) . '/../MIME.php';
+
+/**
+ * The Horde_MIME_Part:: class provides a wrapper around MIME parts and
+ * methods for dealing with them.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@horde.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Part
+{
+    /* The character(s) used internally for EOLs. */
+    const EOL = "\n";
+
+    /* The character string designated by RFC 2045 to designate EOLs in MIME
+     * messages. */
+    const RFC_EOL = "\r\n";
+
+    /* The default MIME character set. */
+    const DEFAULT_CHARSET = 'us-ascii';
+
+    /* The default MIME disposition. */
+    const DEFAULT_DISPOSITION = 'inline';
+
+    /* The default MIME encoding. */
+    const DEFAULT_ENCODING = '7bit';
+
+    /**
+     * The type (ex.: text) of this part.
+     * Per RFC 2045, the default is 'application'.
+     *
+     * @var string
+     */
+    protected $_type = 'application';
+
+    /**
+     * The subtype (ex.: plain) of this part.
+     * Per RFC 2045, the default is 'octet-stream'.
+     *
+     * @var string
+     */
+    protected $_subtype = 'octet-stream';
+
+    /**
+     * The body of the part.
+     *
+     * @var string
+     */
+    protected $_contents = '';
+
+    /**
+     * The desired transfer encoding of this part.
+     *
+     * @var string
+     */
+    protected $_transferEncoding = self::DEFAULT_ENCODING;
+
+    /**
+     * Should the message be encoded via 7-bit?
+     *
+     * @var boolean
+     */
+    protected $_encode7bit = true;
+
+    /**
+     * The description of this part.
+     *
+     * @var string
+     */
+    protected $_description = '';
+
+    /**
+     * The disposition of this part (inline or attachment).
+     *
+     * @var string
+     */
+    protected $_disposition = self::DEFAULT_DISPOSITION;
+
+    /**
+     * The disposition parameters of this part.
+     *
+     * @var array
+     */
+    protected $_dispParams = array();
+
+    /**
+     * The content type parameters of this part.
+     *
+     * @var array
+     */
+    protected $_contentTypeParams = array();
+
+    /**
+     * The subparts of this part.
+     *
+     * @var array
+     */
+    protected $_parts = array();
+
+    /**
+     * Information/Statistics on the subpart.
+     *
+     * @var array
+     */
+    protected $_information = array();
+
+    /**
+     * The list of CIDs for this part.
+     *
+     * @var array
+     */
+    protected $_cids = array();
+
+    /**
+     * The MIME ID of this part.
+     *
+     * @var string
+     */
+    protected $_mimeid = null;
+
+    /**
+     * The sequence to use as EOL for this part.
+     * The default is currently to output the EOL sequence internally as
+     * just "\n" instead of the canonical "\r\n" required in RFC 822 & 2045.
+     * To be RFC complaint, the full <CR><LF> EOL combination should be used
+     * when sending a message.
+     * It is not crucial here since the PHP/PEAR mailing functions will handle
+     * the EOL details.
+     *
+     * @var string
+     */
+    protected $_eol = self::EOL;
+
+    /**
+     * Internal class flags.
+     *
+     * @var array
+     */
+    protected $_flags = array();
+
+    /**
+     * Unique Horde_MIME_Part boundary string.
+     *
+     * @var string
+     */
+    protected $_boundary = null;
+
+    /**
+     * Default value for this Part's size.
+     *
+     * @var integer
+     */
+    protected $_bytes = 0;
+
+    /**
+     * The content-ID for this part.
+     *
+     * @var string
+     */
+    protected $_contentid = null;
+
+    /**
+     * Do we need to reindex the current part.
+     *
+     * @var boolean
+     */
+    protected $_reindex = false;
+
+    /**
+     * TODO
+     */
+    protected $_headers;
+
+    /**
+     * Set the content-disposition of this part.
+     *
+     * @param string $disposition  The content-disposition to set (inline or
+     *                             attachment).
+     */
+    public function setDisposition($disposition)
+    {
+        $disposition = String::lower($disposition);
+
+        if (in_array($disposition, array('inline', 'attachment'))) {
+            $this->_disposition = $disposition;
+        }
+    }
+
+    /**
+     * Get the content-disposition of this part.
+     *
+     * @return string  The part's content-disposition.
+     */
+    public function getDisposition()
+    {
+        return $this->_disposition;
+    }
+
+    /**
+     * Add a disposition parameter to this part.
+     *
+     * @param string $label  The disposition parameter label.
+     * @param string $data   The disposition parameter data.
+     */
+    public function setDispositionParameter($label, $data)
+    {
+        $this->_dispParams[$label] = Horde_MIME::decode($data);
+    }
+
+    /**
+     * Get a disposition parameter from this part.
+     *
+     * @param string $label  The disposition parameter label.
+     *
+     * @return string  The data requested.
+     *                 Returns null if $label is not set.
+     */
+    public function getDispositionParameter($label)
+    {
+        return (isset($this->_dispParams[$label]))
+            ? $this->_dispParams[$label]
+            : null;
+    }
+
+    /**
+     * Get all parameters from the Content-Disposition header.
+     *
+     * @return array  An array of all the parameters
+     *                Returns the empty array if no parameters set.
+     */
+    public function getAllDispositionParameters()
+    {
+        return $this->_dispParams;
+    }
+
+    /**
+     * Set the name of this part.
+     *
+     * @param string $name  The name to set.
+     */
+    public function setName($name)
+    {
+        $this->setContentTypeParameter('name', $name);
+    }
+
+    /**
+     * Get the name of this part.
+     *
+     * @param boolean $default  If the name parameter doesn't exist, should we
+     *                          use the default name from the description
+     *                          parameter?
+     *
+     * @return string  The name of the part.
+     */
+    public function getName($default = false)
+    {
+        $name = $this->getContentTypeParameter('name');
+
+        if ($default && empty($name)) {
+            $name = preg_replace('|\W|', '_', $this->getDescription(false));
+        }
+
+        return $name;
+    }
+
+    /**
+     * Set the body contents of this part.
+     *
+     * @param string $contents  The part body.
+     * @param string $encoding  The current encoding of the contents.
+     */
+    public function setContents($contents, $encoding = null)
+    {
+        $this->_contents = $contents;
+        $this->_flags['contentsSet'] = true;
+        $this->_flags['currentEncoding'] = is_null($encoding) ? $this->getCurrentEncoding() : $encoding;
+    }
+
+    /**
+     * Add to the body contents of this part.
+     *
+     * @param string $contents  The contents to append to the current part
+     *                          body.
+     * @param string $encoding  The current encoding of the contents. If not
+     *                          specified, will try to auto determine the
+     *                          encoding.
+     */
+    public function appendContents($contents, $encoding = null)
+    {
+        if (empty($this->_flags['contentsSet'])) {
+            $this->setContents($contents, $encoding);
+        } else {
+            if (!is_null($encoding) &&
+                ($encoding != $this->getCurrentEncoding())) {
+                $this->setTransferEncoding($encoding);
+                $this->transferDecodeContents();
+            }
+            $this->setContents($this->_contents . $contents, $encoding);
+        }
+    }
+
+    /**
+     * Clears the body contents of this part.
+     */
+    public function clearContents()
+    {
+        $this->_contents = '';
+        unset($this->_flags['contentsSet'], $this->_flags['currentEncoding']);
+    }
+
+    /**
+     * Return the body of the part.
+     *
+     * @return string  The raw body of the part.
+     */
+    public function getContents()
+    {
+        return $this->_contents;
+    }
+
+    /**
+     * Returns the contents in strict RFC 822 & 2045 output - namely, all
+     * newlines end with the canonical <CR><LF> sequence.
+     *
+     * @return string  The raw body of the part, with <CR><LF> EOL..
+     */
+    public function getCanonicalContents()
+    {
+        return $this->replaceEOL($this->_contents, self::RFC_EOL);
+    }
+
+    /**
+     * Transfer encode the contents (to the transfer encoding identified via
+     * getTransferEncoding()) and set as the part's new contents.
+     */
+    public function transferEncodeContents()
+    {
+        $contents = $this->transferEncode();
+        $encode = $this->_flags['currentEncoding'] = $this->_flags['lastTransferEncode'];
+        $this->setContents($contents, $encode);
+        $this->setTransferEncoding($encode);
+    }
+
+    /**
+     * Transfer decode the contents and set them as the new contents.
+     */
+    public function transferDecodeContents()
+    {
+        $contents = $this->transferDecode();
+        $encode = $this->_flags['currentEncoding'] = $this->_flags['lastTransferDecode'];
+        $this->setTransferEncoding($encode);
+
+        /* Don't set contents if they are empty, because this will do stuff
+           like reset the internal bytes field, even though we shouldn't do
+           that (the user has their reasons to set the bytes field to a
+           non-zero value without putting the contents into this part). */
+        if (strlen($contents)) {
+            $this->setContents($contents, $encode);
+        }
+    }
+
+    /**
+     * Set the MIME type of this part.
+     *
+     * @param string $mimetype  The MIME type to set (ex.: text/plain).
+     */
+    public function setType($mimetype)
+    {
+        /* RFC 2045: Any entity with unrecognized encoding must be treated
+           as if it has a Content-Type of "application/octet-stream"
+           regardless of what the Content-Type field actually says. */
+        if ($this->_transferEncoding == 'x-unknown') {
+            return;
+        }
+
+        list($this->_type, $this->_subtype) = explode('/', String::lower($mimetype));
+
+        /* Known types. */
+        $known = array(
+            'text', 'multipart', 'message', 'application', 'audio', 'image',
+            'video', 'model'
+        );
+
+        if (in_array($this->_type, $known)) {
+            /* Set the boundary string for 'multipart/*' parts. */
+            if ($this->_type == 'multipart') {
+                if (!$this->getContentTypeParameter('boundary')) {
+                    $this->setContentTypeParameter('boundary', $this->_generateBoundary());
+                }
+            } else {
+                $this->clearContentTypeParameter('boundary');
+            }
+        } else {
+            $this->_type = 'x-unknown';
+            $this->clearContentTypeParameter('boundary');
+        }
+    }
+
+     /**
+      * Get the full MIME Content-Type of this part.
+      *
+      * @param boolean $charset  Append character set information to the end
+      *                          of the content type if this is a text/* part?
+      *
+      * @return string  The mimetype of this part
+      *                 (ex.: text/plain; charset=us-ascii).
+      */
+     public function getType($charset = false)
+     {
+         if (!isset($this->_type) || !isset($this->_subtype)) {
+             return false;
+         }
+
+         $ptype = $this->getPrimaryType();
+         $type = $ptype . '/' . $this->getSubType();
+         if ($charset && ($ptype == 'text')) {
+             $type .= '; charset=' . $this->getCharset();
+         }
+
+         return $type;
+     }
+
+    /**
+     * If the subtype of a MIME part is unrecognized by an application, the
+     * default type should be used instead (See RFC 2046).  This method
+     * returns the default subtype for a particular primary MIME type.
+     *
+     * @return string  The default MIME type of this part (ex.: text/plain).
+     */
+    public function getDefaultType()
+    {
+        switch ($this->getPrimaryType()) {
+        case 'text':
+            /* RFC 2046 (4.1.4): text parts default to text/plain. */
+            return 'text/plain';
+
+        case 'multipart':
+            /* RFC 2046 (5.1.3): multipart parts default to multipart/mixed. */
+            return 'multipart/mixed';
+
+        default:
+            /* RFC 2046 (4.2, 4.3, 4.4, 4.5.3, 5.2.4): all others default to
+               application/octet-stream. */
+            return 'application/octet-stream';
+        }
+    }
+
+    /**
+     * Get the primary type of this part.
+     *
+     * @return string  The primary MIME type of this part.
+     */
+    public function getPrimaryType()
+    {
+        return $this->_type;
+    }
+
+    /**
+     * Get the subtype of this part.
+     *
+     * @return string  The MIME subtype of this part.
+     */
+    public function getSubType()
+    {
+        return $this->_subtype;
+    }
+
+    /**
+     * Set the character set of this part.
+     *
+     * @param string $charset  The character set of this part.
+     */
+    public function setCharset($charset)
+    {
+        $this->setContentTypeParameter('charset', $charset);
+    }
+
+    /**
+     * Get the character set to use for of this part.  Returns a charset for
+     * all types (not just 'text/*') since we use this charset to determine
+     * how to encode text in MIME headers.
+     *
+     * @return string  The character set of this part.  Returns null if there
+     *                 is no character set.
+     */
+    public function getCharset()
+    {
+        $charset = $this->getContentTypeParameter('charset');
+        return empty($charset) ? null : $charset;
+    }
+
+    /**
+     * Set the description of this part.
+     *
+     * @param string $description  The description of this part.
+     */
+    public function setDescription($description)
+    {
+        $this->_description = Horde_MIME::decode($description);
+    }
+
+    /**
+     * Get the description of this part.
+     *
+     * @param boolean $default  If the name parameter doesn't exist, should we
+     *                          use the default name from the description
+     *                          parameter?
+     *
+     * @return string  The description of this part.
+     */
+    public function getDescription($default = false)
+    {
+        $desc = $this->_description;
+
+        if ($default && empty($desc)) {
+            $desc = $this->getName();
+        }
+
+        return $desc;
+    }
+
+    /**
+     * Set the transfer encoding to use for this part.
+     *
+     * @param string $encoding  The transfer encoding to use.
+     */
+    public function setTransferEncoding($encoding)
+    {
+        $known = array('7bit', '8bit', 'binary', 'base64', 'quoted-printable');
+        $encoding = String::lower($encoding);
+
+        if (in_array($encoding, $known)) {
+            $this->_transferEncoding = $encoding;
+        } else {
+            /* RFC 2045: Any entity with unrecognized encoding must be treated
+               as if it has a Content-Type of "application/octet-stream"
+               regardless of what the Content-Type field actually says. */
+            $this->setType('application/octet-stream');
+            $this->_transferEncoding = 'x-unknown';
+        }
+    }
+
+    /**
+     * Add a MIME subpart.
+     *
+     * @param Horde_MIME_Part $mime_part  Add a subpart to the current object.
+     */
+    public function addPart($mime_part)
+    {
+        $this->_parts[] = $mime_part;
+        $this->_reindex = true;
+    }
+
+    /**
+     * Get a list of all MIME subparts.
+     *
+     * @return array  An array of the Horde_MIME_Part subparts.
+     */
+    public function getParts()
+    {
+        return $this->_parts;
+    }
+
+    /**
+     * Retrieve a specific MIME part.
+     *
+     * @param string $id  The MIME ID.
+     *
+     * @return Horde_MIME_Part  A pointer to the part requested, or null if
+     *                          the part doesn't exist.
+     */
+    public function &getPart($id)
+    {
+        $this_id = $this->getMIMEId();
+        if ($id == $this_id) {
+            return $this;
+        }
+
+        if ($this->_reindex) {
+            $this->buildMIMEIds(is_null($this_id) ? '1' : $this_id);
+        }
+
+        return $this->_partFind($id, $this->_parts);
+    }
+
+    /**
+     * Remove a subpart.
+     *
+     * @param string $id  The MIME part to delete.
+     */
+    public function removePart($id)
+    {
+        $ob = &$this->getPart($id);
+        if ($ob !== null) {
+            unset($ob);
+        }
+    }
+
+    /**
+     * Alter a current MIME subpart.
+     *
+     * @param string $id                  The MIME part ID to alter.
+     * @param Horde_MIME_Part $mime_part  The MIME part to store.
+     */
+    public function alterPart($id, $mime_part)
+    {
+        $ob = &$this->getPart($id);
+        if ($ob !== null) {
+            $ob = $mime_part;
+        }
+    }
+
+    /**
+     * Function used to find a specific MIME part by ID.
+     *
+     * @param string $id     The MIME ID.
+     * @param array &$parts  A list of Horde_MIME_Part objects.
+     */
+    protected function &_partFind($id, &$parts)
+    {
+        foreach (array_keys($parts) as $val) {
+            $ret = $parts[$val]->getPart($id);
+            if ($ret !== null) {
+                return $ret;
+            }
+        }
+
+        $ret = null;
+        return $ret;
+    }
+
+    /**
+     * Add a content type parameter to this part.
+     *
+     * @param string $label  The disposition parameter label.
+     * @param string $data   The disposition parameter data.
+     */
+    public function setContentTypeParameter($label, $data)
+    {
+        $this->_contentTypeParams[$label] = $data;
+    }
+
+    /**
+     * Clears a content type parameter from this part.
+     *
+     * @param string $label  The disposition parameter label.
+     * @param string $data   The disposition parameter data.
+     */
+    public function clearContentTypeParameter($label)
+    {
+        unset($this->_contentTypeParams[$label]);
+    }
+
+    /**
+     * Get a content type parameter from this part.
+     *
+     * @param string $label  The content type parameter label.
+     *
+     * @return string  The data requested.
+     *                 Returns null if $label is not set.
+     */
+    public function getContentTypeParameter($label)
+    {
+        return isset($this->_contentTypeParams[$label])
+            ? $this->_contentTypeParams[$label]
+            : null;
+    }
+
+    /**
+     * Get all parameters from the Content-Type header.
+     *
+     * @return array  An array of all the parameters
+     *                Returns the empty array if no parameters set.
+     */
+    public function getAllContentTypeParameters()
+    {
+        return $this->_contentTypeParameters;
+    }
+
+    /**
+     * Sets a new string to use for EOLs.
+     *
+     * @param string $eol  The string to use for EOLs.
+     */
+    public function setEOL($eol)
+    {
+        $this->_eol = $eol;
+    }
+
+    /**
+     * Get the string to use for EOLs.
+     *
+     * @return string  The string to use for EOLs.
+     */
+    public function getEOL()
+    {
+        return $this->_eol;
+    }
+
+    /**
+     * Returns a Horde_MIME_Header object containing all MIME headers needed
+     * for the part.
+     *
+     * @param Horde_MIME_Headers $headers  The Horde_MIME_Headers object to
+     *                                     add the MIME headers to. If not
+     *                                     specified, adds the headers to a
+     *                                     new object.
+     *
+     * @return Horde_MIME_Headers  A Horde_MIME_Headers object.
+     */
+    public function addMIMEHeaders($headers = null)
+    {
+        if (is_null($headers)) {
+            $headers = new Horde_MIME_Headers();
+        }
+
+        foreach ($this->getHeaderArray() as $key => $val) {
+            $headers->addHeader($key, $val);
+        }
+
+        return $headers;
+    }
+
+    /**
+     * Get the list of MIME headers for this part in an array.
+     *
+     * @return array  The full set of MIME headers.
+     */
+    public function getHeaderArray()
+    {
+        $ptype = $this->getPrimaryType();
+        $stype = $this->getSubType();
+
+        /* Get the character set for this part. */
+        $charset = $this->getCharset();
+
+        /* Get the Content-Type - this is ALWAYS required. */
+        $ctype = $this->getType(true);
+
+        /* Manually encode Content-Type and Disposition parameters in here,
+         * rather than in Horde_MIME_Headers, since it is easier to do when
+         * the paramters are broken down. Encoding in the headers object will
+         * ignore these headers Since they will already be in 7bit. */
+        foreach ($this->getAllContentTypeParameters() as $key => $value) {
+            /* Skip the charset key since that would have already been
+             * added to $ctype by getType(). */
+            if ($key == 'charset') {
+                continue;
+            }
+
+            $encode_2231 = Horde_MIME::encodeParamString($key, $value, $charset);
+            /* Try to work around non RFC 2231-compliant MUAs by sending both
+             * a RFC 2047-like parameter name and then the correct RFC 2231
+             * parameter.  See:
+             * http://lists.horde.org/archives/dev/Week-of-Mon-20040426/014240.html */
+            if (!empty($GLOBALS['conf']['mailformat']['brokenrfc2231']) &&
+                (strpos($encode_2231, '*=') !== false)) {
+                $ctype .= '; ' . $key . '="' . Horde_MIME::encode($value, $charset) . '"';
+            }
+            $ctype .= '; ' . $encode_2231;
+        }
+        $headers['Content-Type'] = $ctype;
+
+        /* Get the description, if any. */
+        if (($descrip = $this->getDescription())) {
+            $headers['Content-Description'] = $descrip;
+        }
+
+        /* RFC 2045 [4] - message/rfc822 and message/partial require the
+           MIME-Version header only if they themselves claim to be MIME
+           compliant. */
+        if (($ptype == 'message') &&
+            (($stype == 'rfc822') || ($stype == 'partial'))) {
+            // TODO - Check for "MIME-Version" in message/rfc822 part.
+            $headers['MIME-Version'] = '1.0';
+        }
+
+        /* message/* parts require no additional header information. */
+        if ($ptype == 'message') {
+            return $headers;
+        }
+
+        /* Don't show Content-Disposition for multipart messages unless
+           there is a name parameter. */
+        $name = $this->getName();
+        if (($ptype != 'multipart') || !empty($name)) {
+            $disp = $this->getDisposition();
+
+            /* Add any disposition parameter information, if available. */
+            if (!empty($name)) {
+                $encode_2231 = Horde_MIME::encodeParamString('filename', $name, $charset);
+                /* Same broken RFC 2231 workaround as above. */
+                if (!empty($GLOBALS['conf']['mailformat']['brokenrfc2231']) &&
+                    (strpos($encode_2231, '*=') !== false)) {
+                    $disp .= '; filename="' . Horde_MIME::encode($name, $charset) . '"';
+                }
+                $disp .= '; ' . $encode_2231;
+            }
+
+            $headers['Content-Disposition'] = $disp;
+        }
+
+        /* Add transfer encoding information. */
+        $headers['Content-Transfer-Encoding'] = $this->getTransferEncoding();
+
+        /* Add content ID information. */
+        if (!is_null($this->_contentid)) {
+            $headers['Content-ID'] = $this->_contentid;
+        }
+
+        return $headers;
+    }
+
+    /**
+     * Return the entire part in MIME format. Includes headers on request.
+     *
+     * @param boolean $headers  Include the MIME headers?
+     *
+     * @return string  The MIME string.
+     */
+    public function toString($headers = true)
+    {
+        $eol = $this->getEOL();
+        $ptype = $this->getPrimaryType();
+        $text = '';
+
+        if ($headers) {
+            $hdr_ob = $this->addMIMEHeaders();
+            $hdr_ob->setEOL($eol);
+            $text = $hdr_ob->toString(array('charset' => $this->getCharset()));
+        }
+
+        /* Any information about a message/* is embedded in the message
+           contents themself. Simply output the contents of the part
+           directly and return. */
+        if ($ptype == 'message') {
+            return $text . $this->_contents;
+        }
+
+        $text .= $this->transferEncode();
+
+        /* Deal with multipart messages. */
+        if ($ptype == 'multipart') {
+            $this->_generateBoundary();
+            $boundary = trim($this->getContentTypeParameter('boundary'), '"');
+            if (!strlen($this->_contents)) {
+                $text .= 'This message is in MIME format.' . $eol;
+            }
+            reset($this->_parts);
+            while (list(,$part) = each($this->_parts)) {
+                $text .= $eol . '--' . $boundary . $eol;
+                $oldEOL = $part->getEOL();
+                $part->setEOL($eol);
+                $text .= $part->toString(true);
+                $part->setEOL($oldEOL);
+            }
+            $text .= $eol . '--' . $boundary . '--' . $eol;
+        }
+
+        return $text;
+    }
+
+    /**
+     * Returns the encoded part in strict RFC 822 & 2045 output - namely, all
+     * newlines end with the canonical <CR><LF> sequence.
+     *
+     * @param boolean $headers  Include the MIME headers?
+     *
+     * @return string  The entire MIME part.
+     */
+    public function toCanonicalString($headers = true)
+    {
+        $string = $this->toString($headers);
+        return $this->replaceEOL($string, self::RFC_EOL);
+    }
+
+    /**
+     * Should we make sure the message is encoded via 7-bit (e.g. to adhere
+     * to mail delivery standards such as RFC 2821)?
+     *
+     * @param boolean $use7bit  Use 7-bit encoding?
+     */
+    public function strict7bit($use7bit)
+    {
+        $this->_encode7bit = $use7bit;
+    }
+
+    /**
+     * Get the transfer encoding for the part based on the user requested
+     * transfer encoding and the current contents of the part.
+     *
+     * @return string  The transfer-encoding of this part.
+     */
+    public function getTransferEncoding()
+    {
+        /* If there are no contents, return whatever the current value of
+           $_transferEncoding is. */
+        if (empty($this->_contents)) {
+            return $encoding;
+        }
+
+        $encoding = $this->_transferEncoding;
+        $ptype = $this->getPrimaryType();
+
+        switch ($ptype) {
+        case 'message':
+            /* RFC 2046 [5.2.1] - message/rfc822 messages only allow 7bit,
+               8bit, and binary encodings. If the current encoding is either
+               base64 or q-p, switch it to 8bit instead.
+               RFC 2046 [5.2.2, 5.2.3, 5.2.4] - All other message/* messages
+               only allow 7bit encodings. */
+            $encoding = ($this->getSubType() == 'rfc822') ? '8bit' : '7bit';
+            break;
+
+        case 'text':
+            if (Horde_MIME::is8bit($this->_contents)) {
+                $encoding = ($this->_encode7bit) ? 'quoted-printable' : '8bit';
+            } elseif (preg_match("/(?:\n|^)[^\n]{999,}(?:\n|$)/", $this->_contents)) {
+                /* If the text is longer than 998 characters between
+                 * linebreaks, use quoted-printable encoding to ensure the
+                 * text will not be chopped (i.e. by sendmail if being sent
+                 * as mail text). */
+                $encoding = 'quoted-printable';
+            }
+            break;
+
+        default:
+            if (Horde_MIME::is8bit($this->_contents)) {
+                $encoding = ($this->_encode7bit) ? 'base64' : '8bit';
+            }
+            break;
+        }
+
+        /* Need to do one last check for binary data if encoding is 7bit or
+         * 8bit.  If the message contains a NULL character at all, the message
+         * MUST be in binary format. RFC 2046 [2.7, 2.8, 2.9]. Q-P and base64
+         * can handle binary data fine so no need to switch those encodings. */
+        if (in_array($encoding, array('8bit', '7bit')) &&
+            preg_match('/\x00/', $this->_encoding)) {
+            $encoding = ($this->_encode7bit) ? 'base64' : 'binary';
+        }
+
+        return $encoding;
+    }
+
+    /**
+     * Retrieves the current encoding of the contents in the object.
+     *
+     * @return string  The current encoding.
+     */
+    public function getCurrentEncoding()
+    {
+        return empty($this->_flags['currentEncoding'])
+            ? $this->_transferEncoding
+            : $this->_flags['currentEncoding'];
+    }
+
+    /**
+     * Encodes the contents with the part's transfer encoding.
+     *
+     * @return string  The encoded text.
+     */
+    public function transferEncode()
+    {
+        $encoding = $this->getTransferEncoding();
+        $eol = $this->getEOL();
+
+        /* Set the 'lastTransferEncode' flag so that transferEncodeContents()
+           can save a call to getTransferEncoding(). */
+        $this->_flags['lastTransferEncode'] = $encoding;
+
+        /* If contents are empty, or contents are already encoded to the
+           correct encoding, return now. */
+        if (!strlen($this->_contents) ||
+            ($encoding == $this->_flags['currentEncoding'])) {
+            return $this->_contents;
+        }
+
+        switch ($encoding) {
+        /* Base64 Encoding: See RFC 2045, section 6.8 */
+        case 'base64':
+            /* Keeping these two lines separate seems to use much less
+               memory than combining them (as of PHP 4.3). */
+            $encoded_contents = base64_encode($this->_contents);
+            return chunk_split($encoded_contents, 76, $eol);
+
+        /* Quoted-Printable Encoding: See RFC 2045, section 6.7 */
+        case 'quoted-printable':
+            $output = Horde_MIME::quotedPrintableEncode($this->_contents, $eol);
+            if (($eollength = String::length($eol)) &&
+                (substr($output, $eollength * -1) == $eol)) {
+                return substr($output, 0, $eollength * -1);
+            }
+            return $output;
+
+        default:
+            return $this->replaceEOL($this->_contents);
+        }
+    }
+
+    /**
+     * Decodes the contents of the part to either a 7bit or 8bit encoding.
+     *
+     * @return string  The decoded text.
+     *                 Returns the empty string if there is no text to decode.
+     */
+    public function transferDecode()
+    {
+        $encoding = $this->getCurrentEncoding();
+
+        /* If the contents are empty, return now. */
+        if (!strlen($this->_contents)) {
+            $this->_flags['lastTransferDecode'] = $encoding;
+            return $this->_contents;
+        }
+
+        switch ($encoding) {
+        case 'base64':
+            $this->_flags['lastTransferDecode'] = '8bit';
+            return base64_decode($this->_contents);
+
+        case 'quoted-printable':
+            $message = preg_replace("/=\r?\n/", '', $this->_contents);
+            $message = quoted_printable_decode($this->replaceEOL($message));
+            $this->_flags['lastTransferDecode'] = (Horde_MIME::is8bit($message)) ? '8bit' : '7bit';
+            return $message;
+
+        /* Support for uuencoded encoding - although not required by RFCs,
+           some mailers may still encode this way. */
+        case 'uuencode':
+        case 'x-uuencode':
+        case 'x-uue':
+            $this->_flags['lastTransferDecode'] = '8bit';
+            return convert_uuencode($this->_contents);
+
+        default:
+            if (isset($this->_flags['lastTransferDecode']) &&
+                ($this->_flags['lastTransferDecode'] != $encoding)) {
+                $message = $this->replaceEOL($this->_contents);
+            } else {
+                $message = $this->_contents;
+            }
+            $this->_flags['lastTransferDecode'] = $encoding;
+            return $message;
+        }
+    }
+
+    /**
+     * Split the contents of the current Part into its respective subparts,
+     * if it is multipart MIME encoding.
+     *
+     * The boundary Content-Type parameter must be set for this function to
+     * work correctly.
+     *
+     * @return boolean  True if the contents were successfully split.
+     *                  False if any error occurred.
+     */
+    public function splitContents()
+    {
+        if ((!($boundary = $this->getContentTypeParameter('boundary'))) ||
+            !strlen($this->_contents)) {
+            return false;
+        }
+
+        $eol = $this->getEOL();
+        $retvalue = false;
+
+        $boundary = '--' . $boundary;
+        if (substr($this->_contents, 0, strlen($boundary)) == $boundary) {
+            $pos1 = 0;
+        } else {
+            $pos1 = strpos($this->_contents, $eol . $boundary);
+            if ($pos1 === false) {
+                return false;
+            }
+        }
+
+        $pos1 = strpos($this->_contents, $eol, $pos1 + 1);
+        if ($pos1 === false) {
+            return false;
+        }
+        $pos1 += strlen($eol);
+
+        reset($this->_parts);
+        $part_ptr = key($this->_parts);
+
+        while ($pos2 = strpos($this->_contents, $eol . $boundary, $pos1)) {
+            $this->_parts[$part_ptr]->setContents(substr($this->_contents, $pos1, $pos2 - $pos1));
+            $this->_parts[$part_ptr]->splitContents();
+            next($this->_parts);
+            $part_ptr = key($this->_parts);
+            if (is_null($part_ptr)) {
+                return false;
+            }
+            $pos1 = strpos($this->_contents, $eol, $pos2 + 1);
+            if ($pos1 === false) {
+                return true;
+            }
+            $pos1 += strlen($eol);
+        }
+
+        return true;
+    }
+
+    /**
+     * Replace newlines in this part's contents with those specified by either
+     * the given newline sequence or the part's current EOL setting.
+     *
+     * @param string $text  The text to replace.
+     * @param string $eol   The EOL sequence to use. If not present, uses the
+     *                      part's current EOL setting.
+     *
+     * @return string  The text with the newlines replaced by the desired
+     *                 newline sequence.
+     */
+    public function replaceEOL($text, $eol = null)
+    {
+        if (is_null($eol)) {
+            $eol = $this->getEOL();
+        }
+        return preg_replace("/\r?\n/", $eol, $text);
+    }
+
+    /**
+     * Determine the size of this MIME part and its child members.
+     *
+     * @return integer  Size of the part, in bytes.
+     */
+    public function getBytes()
+    {
+        $bytes = 0;
+
+        if (empty($this->_flags['contentsSet']) && $this->_bytes) {
+            $bytes = $this->_bytes;
+        } elseif ($this->getPrimaryType() == 'multipart') {
+            reset($this->_parts);
+            while (list(,$part) = each($this->_parts)) {
+                /* Skip multipart entries (since this may result in double
+                   counting). */
+                if ($part->getPrimaryType() != 'multipart') {
+                    $bytes += $part->getBytes();
+                }
+            }
+        } else {
+            $bytes = ($this->getPrimaryType() == 'text')
+                ? String::length($this->_contents, $this->getCharset())
+                : strlen($this->_contents);
+        }
+
+        return $bytes;
+    }
+
+    /**
+     * Explicitly set the size (in bytes) of this part. This value will only
+     * be returned (via getBytes()) if there are no contents currently set.
+     * This function is useful for setting the size of the part when the
+     * contents of the part are not fully loaded (i.e. creating a
+     * Horde_MIME_Part object from IMAP header information without loading the
+     * data of the part).
+     *
+     * @param integer $bytes  The size of this part in bytes.
+     */
+    public function setBytes($bytes)
+    {
+        $this->_bytes = $bytes;
+    }
+
+    /**
+     * Output the size of this MIME part in KB.
+     *
+     * @return string  Size of the part, in string format.
+     */
+    public function getSize()
+    {
+        $bytes = $this->getBytes();
+        if (empty($bytes)) {
+            return $bytes;
+        }
+
+        $localeinfo = NLS::getLocaleInfo();
+        return number_format($bytes / 1024, 2, $localeinfo['decimal_point'], $localeinfo['thousands_sep']);
+     }
+
+    /**
+     * Add to the list of CIDs for this part.
+     *
+     * @param array $cids  A list of MIME IDs of the part.
+     *                     Key - MIME ID
+     *                     Value - CID for the part
+     */
+    public function addCID($cids = array())
+    {
+        $this->_cids += $cids;
+        asort($this->_cids, SORT_STRING);
+    }
+
+    /**
+     * Returns the list of CIDs for this part.
+     *
+     * @return array  The list of CIDs for this part.
+     */
+    public function getCIDList()
+    {
+        return $this->_cids;
+    }
+
+    /**
+     * Sets the Content-ID header for this part.
+     *
+     * @param string $cid  Use this CID (if not already set).  Else, generate
+     *                     a random CID.
+     */
+    public function setContentID($cid = null)
+    {
+        if (is_null($this->_contentid)) {
+            $this->_contentid = (is_null($cid)) ? (Horde_MIME::generateRandomID() . '@' . $_SERVER['SERVER_NAME']) : $cid;
+        }
+        return $this->_contentid;
+    }
+
+    /**
+     * Returns the Content-ID for this part.
+     *
+     * @return string  The Content-ID for this part.
+     */
+    public function getContentID()
+    {
+        return $this->_contentid;
+    }
+
+    /**
+     * Alter the MIME ID of this part.
+     *
+     * @param string $mimeid  The MIME ID.
+     */
+    public function setMIMEId($mimeid)
+    {
+        $this->_mimeid = $mimeid;
+    }
+
+    /**
+     * Returns the MIME ID of this part.
+     *
+     * @return string  The MIME ID.
+     */
+    public function getMIMEId()
+    {
+        return $this->_mimeid;
+    }
+
+    /**
+     * Build the MIME IDs for this part and all subparts.
+     *
+     * @param string $id  The ID of this part.
+     */
+    public function buildMIMEIds($id = null)
+    {
+        if (is_null($id)) {
+            if (empty($this->_parts)) {
+                $this->setMIMEId('1');
+            } else {
+                $this->setMIMEId('0');
+                $i = 1;
+                foreach (array_keys($this->_parts) as $val) {
+                    $this->_parts[$val]->buildMIMEIds($i++);
+                }
+            }
+        } else {
+            $this->setMIMEId($id . (($this->getType() == 'message/rfc822') ? '.0' : ''));
+
+            if (!empty($this->_parts)) {
+                $i = 1;
+                foreach (array_keys($this->_parts) as $val) {
+                    $this->_parts[$val]->buildMIMEIds($id . '.' . $i++);
+                }
+            }
+        }
+
+        $this->_reindex = false;
+    }
+
+    /**
+     * Generate the unique boundary string (if not already done).
+     *
+     * @return string  The boundary string.
+     */
+    protected function _generateBoundary()
+    {
+        if (is_null($this->_boundary)) {
+            $this->_boundary = '=_' . Horde_MIME::generateRandomID();
+        }
+        return $this->_boundary;
+    }
+
+    /**
+     * Returns a mapping of all MIME IDs to their content-types.
+     *
+     * @param boolean $sort  Sort by MIME ID?
+     *
+     * @return array  Keys: MIME ID; values: content type.
+     */
+    public function contentTypeMap($sort = true)
+    {
+        $map = array($this->getMIMEId() => $this->getType());
+        foreach ($this->_parts as $val) {
+            $map += $val->contentTypeMap(false);
+        }
+
+        if ($sort) {
+            uksort($map, 'strnatcasecmp');
+        }
+
+        return $map;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer.php b/framework/mime_tmp/Horde/Mime/Viewer.php
new file mode 100644 (file)
index 0000000..cfc47fc
--- /dev/null
@@ -0,0 +1,332 @@
+<?php
+/**
+ * The Horde_MIME_Viewer:: class provides an abstracted interface to render
+ * MIME data into various formats.  It depends on both a set of
+ * Horde_MIME_Viewer_* drivers which handle the actual rendering, and a
+ * configuration file to map MIME types to drivers.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Viewer
+{
+    /**
+     * The config array. This array is shared between all instances of
+     * Horde_MIME_Viewer.
+     *
+     * @var array
+     */
+    static protected $_config = array();
+
+    /**
+     * The driver cache array.
+     *
+     * @var array
+     */
+    static protected $_drivercache = array();
+
+    /**
+     * Attempts to return a concrete Horde_MIME_Viewer_* object based on the
+     * MIME type.
+     *
+     * @param string $mime_type  The MIME type.
+     *
+     * @return Horde_MIME_Viewer  The Horde_MIME_Viewer object, or false on
+     *                            error.
+     */
+    static final public function factory($mime_type)
+    {
+        /* Spawn the relevant driver, and return it (or false on failure). */
+        if (($ob = self::_getDriver($mime_type, $GLOBALS['registry']->getApp())) &&
+            self::_resolveDriver($ob['driver'], $ob['app']) &&
+            class_exists($ob['class'])) {
+            return new $ob['class'](self::$_config['mime_drivers'][$ob['app']][$ob['driver']]);
+        }
+
+        return false;
+    }
+
+    /**
+     * Given a MIME type and an app name, determine which driver can best
+     * handle the data.
+     *
+     * @param string $mime_type  MIME type to resolve.
+     * @param string $app        App in which to search for the driver.
+     *
+     * @return mixed  Returns false if driver could not be found. Otherwise,
+     *                an array with the following elements:
+     * <pre>
+     * 'app' - (string) The app containing the driver (e.g. 'horde')
+     * 'driver' - (string) Name of driver (e.g. 'enscript')
+     * 'exact' - (boolean) Was the driver and exact match?
+     * </pre>
+     */
+    static final protected function _getDriver($mime_type, $app = 'horde')
+    {
+        $sig = $mime_type . '|' . $app;
+        if (isset(self::$_drivercache[$sig])) {
+            return self::$_drivercache[$sig];
+        }
+
+        /* Make sure 'horde' mime_drivers config is loaded. */
+        if (empty(self::$_config['mime_drivers']['horde'])) {
+            $res = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), 'horde');
+            if (is_a($res, 'PEAR_Error')) {
+                return false;
+            }
+            self::$_config = $res;
+        }
+
+        /* Make sure app's' mime_drivers config is loaded. */
+        if (($app != 'horde') && empty(self::$_config['mime_drivers'][$app])) {
+            $res = Horde::loadConfiguration('mime_drivers.php', array('mime_drivers', 'mime_drivers_map'), $app);
+            if (is_a($res, 'PEAR_Error')) {
+                return false;
+            }
+
+            require_once 'Horde/Array.php';
+            self::$_config = Horde_Array::array_merge_recursive_overwrite(self::$_config, $res);
+        }
+
+        $driver = '';
+        $exact = false;
+
+        list($primary_type,) = explode('/', $mime_type, 2);
+        $allSub = $primary_type . '/*';
+
+        /* If the app doesn't exist in $mime_drivers_map, check for Horde-wide
+         * viewers. */
+        if (!isset(self::$_config['mime_drivers_map'][$app]) &&
+            ($app != 'horde')) {
+            return self::_getDriver($mime_type, 'horde');
+        }
+
+        $dr = self::$_config['mime_drivers'][$app];
+        $map = self::$_config['mime_drivers_map'][$app];
+
+        /* If an override exists for this MIME type, then use that */
+        if (isset($map['overrides'][$mime_type])) {
+            $driver = $map['overrides'][$mime_type];
+            $exact = true;
+        } elseif (isset($map['overrides'][$allSub])) {
+            $driver = $map['overrides'][$allSub];
+            $exact = true;
+        } elseif (isset($map['registered'])) {
+            /* Iterate through the list of registered drivers, and see if
+             * this MIME type exists in the MIME types that they claim to
+             * handle. If the driver handles it, then assign it as the
+             * rendering driver. If we find a generic handler, keep iterating
+             * to see if we can find a specific handler. */
+            foreach ($map['registered'] as $val) {
+                if (in_array($mime_type, $dr[$val]['handles'])) {
+                    $driver = $val;
+                    $exact = true;
+                    break;
+                } elseif (in_array($allSub, $dr[$val]['handles'])) {
+                    $driver = $val;
+                }
+            }
+        }
+
+        /* If this is an application specific app, and an exact match was
+           not found, search for a Horde-wide specific driver. Only use the
+           Horde-specific driver if it is NOT the 'default' driver AND the
+           Horde driver is an exact match. */
+        if (!$exact && ($app != 'horde')) {
+            $ob = self::_getDriver($mime_type, 'horde');
+            if (empty($driver) ||
+                (($ob['driver'] != 'default') && $ob['exact'])) {
+                $driver = $ob['driver'];
+                $app = 'horde';
+            }
+        }
+
+        /* If the 'default' driver exists in this app, fall back to that. */
+        if (empty($driver) && self::_resolveDriver('default', $app)) {
+            $driver = 'default';
+        }
+
+        if (empty($driver)) {
+            return false;
+        }
+
+        self::$_drivercache[$sig] = array(
+            'app' => $app,
+            'class' => (($app == 'horde') ? '' : $app . '_') . 'Horde_MIME_Viewer_' . $driver,
+            'driver' => $driver,
+            'exact' => $exact,
+        );
+
+        return self::$_drivercache[$sig];
+    }
+
+    /**
+     * Given a driver and an application, attempts to load the library file.
+     *
+     * @param string $driver  Driver name.
+     * @param string $app     Application name.
+     *
+     * @return boolean  True if library file was loaded.
+     */
+    static final protected function _resolveDriver($driver = 'default',
+                                                   $app = 'horde')
+    {
+        $file = ($app == 'horde')
+            ? dirname(__FILE__) . '/Viewer/' . $driver . '.php'
+            : $GLOBALS['registry']->applications[$app]['fileroot'] . '/lib/MIME/Viewer/' . $driver . '.php';
+
+        require_once dirname(__FILE__) . '/Viewer/Driver.php';
+
+        $old_error = error_reporting(0);
+        $ret = require_once $file;
+        error_reporting($old_error);
+
+        return $ret;
+    }
+
+    /**
+     * Prints out the status message for a given MIME Part.
+     *
+     * @param string $msg     The message to output.
+     * @param string $img     An image link to add to the beginning of the
+     *                        message.
+     * @param string $class   An optional style for the status box.
+     *
+     * @return string  The formatted status message string.
+     */
+    static public function formatStatusMsg($msg, $img = null, $class = null)
+    {
+        if (empty($msg)) {
+            return '';
+        }
+
+        if (!is_array($msg)) {
+            $msg = array($msg);
+        }
+
+        /* If we are viewing as an attachment, don't print HTML code. */
+        if (self::viewAsAttachment()) {
+            return implode("\n", $msg);
+        }
+
+        if (is_null($class)) {
+            $class = 'mimeStatusMessage';
+        }
+        $text = '<table class="' . $class . '">';
+
+        /* If no image, simply print out the message. */
+        if (is_null($img)) {
+            foreach ($msg as $val) {
+                $text .= '<tr><td>' . $val . '</td></tr>' . "\n";
+            }
+        } else {
+            $text .= '<tr><td class="mimeStatusIcon">' . $img . '</td><td>';
+            if (count($msg) == 1) {
+                $text .= $msg[0];
+            } else {
+                $text .= '<table>';
+                foreach ($msg as $val) {
+                    $text .= '<tr><td>' . $val . '</td></tr>' . "\n";
+                }
+                $text .= '</table>';
+            }
+            $text .= '</td></tr>' . "\n";
+        }
+
+        return $text . '</table>';
+    }
+
+    /**
+     * Given a MIME type, this function will return an appropriate icon.
+     *
+     * @param string $mime_type  The MIME type that we need an icon for.
+     *
+     * @return string  The URL to the appropriate icon.
+     */
+    static final public function getIcon($mime_type)
+    {
+        $app = $GLOBALS['registry']->getApp();
+        $ob = self::_getIcon($mime_type, $app);
+
+        if (is_null($ob)) {
+            if ($app == 'horde') {
+                return null;
+            }
+
+            $obHorde = self::_getIcon($mime_type, 'horde');
+            return is_null($obHorde) ? null : $obHorde['url'];
+        } elseif (($ob['match'] !== 0) && ($app != 'horde')) {
+            $obHorde = self::_getIcon($mime_type, 'horde');
+            if (!is_null($ob['match']) &&
+                ($obHorde['match'] <= $ob['match'])) {
+                return $obHorde['url'];
+            }
+        }
+
+        return $ob['url'];
+    }
+
+    /**
+     * Given an input MIME type and app, this function returns the URL of an
+     * icon that can be associated with it
+     *
+     * @param string $mime_type  MIME type to get the icon for.
+     *
+     * @return mixed  Null if not found, or an array with the following keys:
+     * <pre>
+     * 'exact' - (integer) How exact the match is.
+     *           0 => 'exact', 1 => 'primary', 2 => 'driver',
+     *           3 => 'default', or null.
+     * 'url' - (string) URL to an icon, or null if none could be found.
+     * </pre>
+     */
+    static final protected function _getIcon($mime_type, $app = 'horde')
+    {
+        if (!($ob = self::_getDriver($mime_type, $app))) {
+            return null;
+        }
+        $driver = $ob['driver'];
+
+        list($primary_type,) = explode('/', $mime_type, 2);
+        $allSub = $primary_type . '/*';
+        $ret = null;
+
+        /* If the app doesn't exist in $mime_drivers, return now. */
+        if (!isset(self::$_config['mime_drivers'][$app])) {
+            return null;
+        }
+
+        $dr = self::$_config['mime_drivers'][$app];
+
+        /* If a specific icon for this driver and mimetype is defined,
+           then use that. */
+        if (isset($dr[$driver]['icons'])) {
+            $icondr = $dr[$driver]['icons'];
+            $iconList = array($mime_type => 0, $allSub => 1, 'default' => 2);
+            foreach ($iconList as $key => $val) {
+                if (isset($icondr[$key])) {
+                    $ret = array('match' => $val, 'url' => $icondr[$key]);
+                    break;
+                }
+            }
+        }
+
+        /* Try to use a default icon if none already obtained. */
+        if (is_null($ret['url']) &&
+            isset($dr['default']['icons']['default'])) {
+            $ret = array('match' => 3, 'url' => $dr['default']['icons']['default']);
+        }
+
+        if (!is_null($ret)) {
+            $ret['url'] = $GLOBALS['registry']->getImageDir($app) . '/mime/' . $ret['url'];
+        }
+
+        return $ret;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/Driver.php b/framework/mime_tmp/Horde/Mime/Viewer/Driver.php
new file mode 100644 (file)
index 0000000..e441e27
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_Driver:: class provides the API for specific viewer
+ * drivers to extend.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME
+ */
+class Horde_MIME_Viewer_Driver
+{
+    /**
+     * Viewer configuration.
+     *
+     * @var array
+     */
+    protected $_conf = array();
+
+    /**
+     * The Horde_MIME_Part object to render.
+     *
+     * @var Horde_MIME_Part
+     */
+    protected $_mimepart = null;
+
+    /**
+     * Viewer parameters.
+     *
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * Can this driver render various views?
+     *
+     * @var boolean
+     */
+    protected $_canrender = array(
+        'full' => false,
+        'info' => false,
+        'inline' => false,
+    );
+
+    /**
+     * Constructor.
+     *
+     * @param array $conf  Configuration specific to the driver.
+     */
+    function __construct($conf = array())
+    {
+        $this->_conf = $conf;
+    }
+
+    /**
+     * Sets the Horde_MIME_Part object for the class.
+     *
+     * @param Horde_MIME_Part &$mime_part  Reference to an object with the
+     *                                     information to be rendered.
+     */
+    public function setMIMEPart(&$mime_part)
+    {
+        $this->_mimepart = $mime_part;
+    }
+
+    /**
+     * Set parameters for use with this object.
+     *
+     * @param array $params  An array of params to add to the internal
+     *                       params list.
+     */
+    public function setParams($params = array())
+    {
+        $this->_params = array_merge($this->_params, $params);
+    }
+
+    /**
+     * Return the rendered version of the Horde_MIME_Part object.
+     *
+     * @return array  TODO
+     */
+    public function render()
+    {
+        return (is_null($this->_mimepart) || !$this->canDisplay())
+            ? array('data' => '', 'type' => 'text/plain')
+            : $this->_render();
+    }
+
+    /**
+     * Return the rendered version of the Horde_MIME_Part object.
+     *
+     * @return string  Rendered version of the Horde_MIME_Part object.
+     */
+    protected function _render()
+    {
+    }
+
+    /**
+     * Return the rendered inline version of the Horde_MIME_Part object.
+     *
+     * @return string  Rendered version of the Horde_MIME_Part object.
+     */
+    public function renderInline()
+    {
+        return (is_null($this->_mimepart) || !$this->canDisplayInline())
+            ? ''
+            : $this->_renderInline();
+    }
+
+    /**
+     * Return the rendered inline version of the Horde_MIME_Part object.
+     *
+     * @return string  Rendered version of the Horde_MIME_Part object.
+     */
+    protected function _renderInline()
+    {
+    }
+
+    /**
+     * Return the rendered information about the Horde_MIME_Part object.
+     *
+     * @return string  Rendered information on the Horde_MIME_Part object.
+     */
+    public function renderInfo()
+    {
+        return (is_null($this->_mimepart) || !$this->canDisplayInfo())
+            ? ''
+            : $this->_renderInfo();
+    }
+
+    /**
+     * Return the rendered information about the Horde_MIME_Part object.
+     *
+     * @return string  Rendered information on the Horde_MIME_Part object.
+     */
+    protected function _renderInfo()
+    {
+    }
+
+    /**
+     * Can this driver render the the data?
+     *
+     * @return boolean  True if the driver can render data.
+     */
+    public function canDisplay()
+    {
+        return $this->_canrender['full'];
+    }
+
+    /**
+     * Can this driver render the the data inline?
+     *
+     * @return boolean  True if the driver can display inline.
+     */
+    public function canDisplayInline()
+    {
+        return $this->getConfigParam('inline') && $this->_canrender['inline'];
+    }
+
+    /**
+     * Can this driver render the the data inline?
+     *
+     * @return boolean  True if the driver can display inline.
+     */
+    public function canDisplayInfo()
+    {
+        return $this->_canrender['info'];
+    }
+
+    /**
+     * Return a configuration parameter for the current viewer.
+     *
+     * @param string $param  The parameter name.
+     *
+     * @return mixed  The value of the parameter; returns null if the
+     *                parameter doesn't exist.
+     */
+    public function getConfigParam($param)
+    {
+        return isset($this->_conf[$param]) ? $this->_conf[$param] : null;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/audio.php b/framework/mime_tmp/Horde/Mime/Viewer/audio.php
new file mode 100644 (file)
index 0000000..df83fbe
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_audio class sends audio parts to the browser for
+ * handling by the browser, a plugin, or a helper application.
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_audio extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Return the content-type.
+     *
+     * @return string  The content-type of the output.
+     */
+    public function getType()
+    {
+        return $this->mime_part->getType();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/css.php b/framework/mime_tmp/Horde/Mime/Viewer/css.php
new file mode 100644 (file)
index 0000000..d11ebed
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+
+require_once dirname(__FILE__) . '/source.php';
+
+/**
+ * The Horde_MIME_Viewer_css class renders CSS source as HTML with an effort
+ * to remove potentially malicious code.
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.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@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_css extends Horde_MIME_Viewer_source
+{
+    /**
+     * Render out the currently set contents.
+     *
+     * @param array $params  Any parameters the viewer may need.
+     *
+     * @return string  The rendered text.
+     */
+    public function render($params = null)
+    {
+        $css = htmlspecialchars($this->mime_part->getContents(), ENT_NOQUOTES);
+        $css = preg_replace_callback('!(}|\*/).*?({|/\*)!s', array($this, '_handles'), $css);
+        $css = preg_replace_callback('!{[^}]*}!s', array($this, '_attributes'), $css);
+        $css = preg_replace_callback('!/\*.*?\*/!s', array($this, '_comments'), $css);
+        $css = trim($css);
+
+        // Educated Guess at whether we are inline or not.
+        if (headers_sent() || ob_get_length()) {
+            return $this->lineNumber($css);
+        } else {
+            return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') .
+                $this->lineNumber($css) .
+                Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc');
+        }
+    }
+
+    /**
+     */
+    protected function _comments($matches)
+    {
+        $patterns[] = '!(http://[/\w-.]+)!s';
+        $replaces[] = '<a href="\\1">\\1</a>';
+
+        $comments = preg_replace($patterns, $replaces, $matches[0]);
+
+        return '<span class="comment">' . $comments . '</span>';
+    }
+
+    /**
+     */
+    protected function _attributes($matches)
+    {
+        // Attributes.
+        $patterns[] = '!([-\w]+\s*):!s';
+        $replaces[] = '<span class="attr"">\\1</span>:';
+
+        // Values.
+        $patterns[] = '!:(\s*)(.+?)(\s*;)!s';
+        $replaces[] = ':\\1<span class="value">\\2</span><span class="eol">\\3</span>';
+
+        // URLs.
+        $patterns[] = '!(url\([\'"]?)(.*?)([\'"]?\))!s';
+        $replaces[] = '<span class="url">\\1<span class="file">\\2</span>\\3</span>';
+
+        // Colors.
+        $patterns[] = '!(#[[:xdigit:]]{3,6})!s';
+        $replaces[] = '<span class="color">\\1</span>';
+
+        // Parentheses.
+        $patterns[] = '!({|})!s';
+        $replaces[] = '<span class="parentheses">\\1</span>';
+
+        // Unity.
+        $patterns[] = '!(em|px|%)\b!s';
+        $replaces[] = '<em>\\1</em>';
+
+        return preg_replace($patterns, $replaces, $matches[0]);
+    }
+
+    /**
+     */
+    protected function _handles($matches)
+    {
+        // HTML Tags.
+        $patterns[] = '!\b(body|h\d|a|span|div|acronym|small|strong|em|pre|ul|ol|li|p)\b!s';
+        $replaces[] = '<span class="htag">\\1</span>\\2';
+
+        // IDs.
+        $patterns[] = '!(#[-\w]+)!s';
+        $replaces[] = '<span class="id">\\1</span>';
+
+        // Class.
+        $patterns[] = '!(\.[-\w]+)\b!s';
+        $replaces[] = '<span class="class">\\1</span>';
+
+        // METAs.
+        $patterns[] = '!(:link|:visited|:hover|:active|:first-letter)!s';
+        $replaces[] = '<span class="metac">\\1</span>';
+
+        return preg_replace($patterns, $replaces, $matches[0]);
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/deb.php b/framework/mime_tmp/Horde/Mime/Viewer/deb.php
new file mode 100644 (file)
index 0000000..7afdc97
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_deb class renders out lists of files in Debian
+ * packages by using the dpkg tool to query the package.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_deb extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render the data.
+     *
+     * @param array $params  Any parameters the viewer may need.
+     *
+     * @return string  The rendered data.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['deb']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['mime_drivers']['horde']['deb']['location'] . " -f $tmp_deb 2>&1", 'r');
+        while (($rc = fgets($fh, 8192))) {
+            $data .= $rc;
+        }
+        pclose($fh);
+
+        return '<pre>' . htmlspecialchars($data) . '</pre>';
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/default.php b/framework/mime_tmp/Horde/Mime/Viewer/default.php
new file mode 100644 (file)
index 0000000..4872f75
--- /dev/null
@@ -0,0 +1,17 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_default class simply prints out the encapsulated
+ * content.  It exists as a fallback if no other intelligent rendering
+ * mechanism could be used.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_default extends Horde_MIME_Viewer_Driver
+{
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/enriched.php b/framework/mime_tmp/Horde/Mime/Viewer/enriched.php
new file mode 100644 (file)
index 0000000..a46db58
--- /dev/null
@@ -0,0 +1,158 @@
+<?php
+/**
+ * The Horde_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.
+ *
+ * Copyright 2001-2008 The Horde Project (http://www.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  Eric Rostetter <eric.rostetter@physics.utexas.edu>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_enriched extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * 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.
+     */
+    public 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('/(?<!&lt;)&lt;bold.*&gt;(.*)&lt;\/bold&gt;/Uis', '<span style="font-weight: bold">\1</span>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;italic.*&gt;(.*)&lt;\/italic&gt;/Uis', '<span style="font-style: italic">\1</span>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;underline.*&gt;(.*)&lt;\/underline&gt;/Uis', '<span style="text-decoration: underline">\1</span>', $text);
+        $text = preg_replace_callback('/(?<!&lt;)&lt;color r=([\da-fA-F]+) g=([\da-fA-F]+) b=([\da-fA-F]+)&gt;(.*)&lt;\/color&gt;/Uis', array($this, 'colorize'), $text);
+        $text = preg_replace('/(?<!&lt;)&lt;color n=(red|blue|green|yellow|cyan|magenta|black|white)&gt;(.*)&lt;\/color&gt;/Uis', '<span style="color: \1">\2</span>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;fontfamily&gt;(.*)&lt;\/fontfamily&gt;/Uis', '\1', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;fontfamily f=(\w+)&gt;(.*)&lt;\/fontfamily&gt;/Uis', '<span style="font-family: \1">\2</span>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;smaller.*&gt;/Uis', '<span style="font-size: smaller">', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;\/smaller&gt;/Uis', '</span>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;bigger.*&gt;/Uis', '<span style="font-size: larger">', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;\/bigger&gt;/Uis', '</span>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;fixed.*&gt;(.*)&lt;\/fixed&gt;/Uis', '<font face="fixed">\1</font>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;center.*&gt;(.*)&lt;\/center&gt;/Uis', '<div align="center">\1</div>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;flushleft.*&gt;(.*)&lt;\/flushleft&gt;/Uis', '<div align="left">\1</div>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;flushright.*&gt;(.*)&lt;\/flushright&gt;/Uis', '<div align="right">\1</div>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;flushboth.*&gt;(.*)&lt;\/flushboth&gt;/Uis', '<div align="justify">\1</div>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;paraindent.*&gt;(.*)&lt;\/paraindent&gt;/Uis', '<blockquote>\1</blockquote>', $text);
+        $text = preg_replace('/(?<!&lt;)&lt;excerpt.*&gt;(.*)&lt;\/excerpt&gt;/Uis', '<blockquote>\1</blockquote>', $text);
+
+        // Replace << with < now (from translated HTML form).
+        $text = str_replace('&lt;&lt;', '&lt;', $text);
+
+        // Now we remove the leading/trailing space we added at the
+        // start.
+        $text = preg_replace('/^ (.*) $/s', '\1', $text);
+
+        // Make URLs clickable.
+        require_once 'Horde/Text/Filter.php';
+        $text = Text_Filter::filter($text, 'linkurls', array('callback' => 'Horde::externalUrl'));
+
+        // Wordwrap -- note this could impact on our above RFC
+        // compliance *IF* we honored nofill tags (which we don't
+        // yet).
+        $text = str_replace("\t", '        ', $text);
+        $text = str_replace('  ', ' &nbsp;', $text);
+        $text = str_replace("\n ", "\n&nbsp;", $text);
+        if ($text[0] == ' ') {
+            $text = '&nbsp;' . substr($text, 1);
+        }
+        $text = nl2br($text);
+        $text = '<p class="fixed">' . $text . '</p>';
+
+        return $text;
+    }
+
+    /**
+     */
+    public 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.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/enscript.php b/framework/mime_tmp/Horde/Mime/Viewer/enscript.php
new file mode 100644 (file)
index 0000000..7d3edb1
--- /dev/null
@@ -0,0 +1,132 @@
+<?php
+
+require_once dirname(__FILE__) . '/source.php';
+
+/**
+ * The Horde_MIME_Viewer_enscript class renders out various content in HTML
+ * format by using GNU Enscript.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_enscript extends Horde_MIME_Viewer_source
+{
+    /**
+     * Render out the data using Enscript.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered data.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['enscript']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['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 = trim($res_arr[1]);
+        }
+
+        /* Educated Guess at whether we are inline or not. */
+        if (headers_sent() || ob_get_length()) {
+            return $this->lineNumber($results);
+        } else {
+            return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') .
+                $this->lineNumber($results) .
+                Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc');
+        }
+    }
+
+    /**
+     * Attempts to determine what language to use for the enscript program
+     * from a MIME type.
+     *
+     * @param string $type  The MIME type.
+     *
+     * @return string  The enscript 'language' parameter string.
+     */
+    protected function _typeToLang($type)
+    {
+        include_once dirname(__FILE__) . '/../Magic.php';
+
+        $ext = Horde_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 'cgi':
+        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;
+        }
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/html.php b/framework/mime_tmp/Horde/Mime/Viewer/html.php
new file mode 100644 (file)
index 0000000..b8317b7
--- /dev/null
@@ -0,0 +1,166 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_html class renders out HTML text with an effort to
+ * remove potentially malicious code.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @author  Jon Parise <jon@horde.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_html extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Can this driver render various views?
+     *
+     * @var boolean
+     */
+    protected $_canrender = array(
+        'full' => true,
+        'info' => false,
+        'inline' => true,
+    );
+
+    /**
+     * Render out the currently set contents.
+     *
+     * @return string  The rendered text.
+     */
+    public function _render()
+    {
+        return array('data' => $this->_cleanHTML($this->_mimepart->getContents(), false), 'type' => $this->_mimepart->getType(true));
+    }
+
+    /**
+     * TODO
+     */
+    public function _renderInline()
+    {
+        return String::convertCharset($this->_cleanHTML($this->_mimepart->getContents(), true), $this->_mimepart->getCharset());
+    }
+
+    /**
+     * Filters active content, dereferences external links, detects phishing,
+     * etc.
+     *
+     * @todo Use IP checks from
+     * http://lxr.mozilla.org/mailnews/source/mail/base/content/phishingDetector.js.
+     *
+     * @param string $data     The HTML data.
+     * @param boolean $inline  Are we viewing inline?
+     *
+     * @return string  The cleaned HTML data.
+     */
+    protected function _cleanHTML($data, $inline)
+    {
+        global $browser, $prefs;
+
+        $phish_warn = false;
+
+        /* Deal with <base> tags in the HTML, since they will screw up our own
+         * relative paths. */
+        if (preg_match('/<base href="?([^"> ]*)"? ?\/?>/i', $data, $matches)) {
+            $base = $matches[1];
+            if (substr($base, -1) != '/') {
+                $base .= '/';
+            }
+
+            /* Recursively call _cleanHTML() to prevent clever fiends from
+             * sneaking nasty things into the page via $base. */
+            $base = $this->_cleanHTML($base, $inline);
+
+            /* Attempt to fix paths that were relying on a <base> tag. */
+            if (!empty($base)) {
+                $pattern = array('|src=(["\'])([^:"\']+)\1|i',
+                                 '|src=([^: >"\']+)|i',
+                                 '|href= *(["\'])([^:"\']+)\1|i',
+                                 '|href=([^: >"\']+)|i');
+                $replace = array('src=\1' . $base . '\2\1',
+                                 'src=' . $base . '\1',
+                                 'href=\1' . $base . '\2\1',
+                                 'href=' . $base . '\1');
+                $data = preg_replace($pattern, $replace, $data);
+            }
+        }
+
+        require_once 'Horde/Text/Filter.php';
+        $strip_style_attributes = (($browser->isBrowser('mozilla') &&
+                                    $browser->getMajor() == 4) ||
+                                   $browser->isBrowser('msie'));
+        $strip_styles = $inline || $strip_style_attributes;
+        $data = Text_Filter::filter($data, 'xss',
+                                    array('body_only' => $inline,
+                                          'strip_styles' => $strip_styles,
+                                          'strip_style_attributes' => $strip_style_attributes));
+
+        /* Check for phishing exploits. */
+        if ($this->getConfigParam('phishing_check')) {
+            if (preg_match('/href\s*=\s*["\']?\s*(http|https|ftp):\/\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:[^>]*>\s*(?:\\1:\/\/)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})[^<]*<\/a)?/i', $data, $m)) {
+                /* Check 1: Check for IP address links, but ignore if the link
+                 * text has the same IP address. */
+                if (!isset($m[3]) || ($m[2] != $m[3])) {
+                    if (isset($m[3])) {
+                        $data = preg_replace('/href\s*=\s*["\']?\s*(http|https|ftp):\/\/' . preg_quote($m[2], '/') . '(?:[^>]*>\s*(?:$1:\/\/)?' . preg_quote($m[3], '/') . '[^<]*<\/a)?/i', 'class="mimeStatusWarning" $0', $data);
+                    }
+                    $phish_warn = true;
+                }
+            } elseif (preg_match_all('/href\s*=\s*["\']?\s*(?:http|https|ftp):\/\/([^\s"\'>]+)["\']?[^>]*>\s*(?:(?:http|https|ftp):\/\/)?(.*?)<\/a/is', $data, $m)) {
+                /* $m[1] = Link; $m[2] = Target
+                 * Check 2: Check for links that point to a different host than
+                 * the target url; if target looks like a domain name, check it
+                 * against the link. */
+                for ($i = 0, $links = count($m[0]); $i < $links; ++$i) {
+                    $link = strtolower(urldecode($m[1][$i]));
+                    $target = strtolower(preg_replace('/^(http|https|ftp):\/\//', '', strip_tags($m[2][$i])));
+                    if (preg_match('/^[-._\da-z]+\.[a-z]{2,}/i', $target) &&
+                        (strpos($link, $target) !== 0) &&
+                        (strpos($target, $link) !== 0)) {
+                        /* Don't consider the link a phishing link if the
+                         * domain is the same on both links (e.g.
+                         * adtracking.example.com & www.example.com). */
+                        preg_match('/\.?([^\.\/]+\.[^\.\/]+)[\/?]/', $link, $host1);
+                        preg_match('/\.?([^\.\/]+\.[^\.\/ ]+)([\/ ].*)?$/', $target, $host2);
+                        if (!(count($host1) && count($host2)) ||
+                            (strcasecmp($host1[1], $host2[1]) !== 0)) {
+                            $data = preg_replace('/href\s*=\s*["\']?\s*(?:http|https|ftp):\/\/' . preg_quote($m[1][$i], '/') . '["\']?[^>]*>\s*(?:(?:http|https|ftp):\/\/)?' . preg_quote($m[2][$i], '/') . '<\/a/is', 'class="mimeStatusWarning" $0', $data);
+                            $phish_warn = true;
+                        }
+                    }
+                }
+            }
+        }
+
+        /* Try to derefer all external references. */
+        $data = preg_replace_callback('/href\s*=\s*(["\'])?((?(1)[^\1]*?|[^\s>]+))(?(1)\1|)/i',
+                                      array($this, '_dereferExternalReferencesCallback'),
+                                      $data);
+
+        /* Prepend phishing warning. */
+        if ($phish_warn) {
+            $phish_warning = sprintf(_("%s: This message may not be from whom it claims to be. Beware of following any links in it or of providing the sender with any personal information.") . ' ' . _("The links that caused this warning have the same background color as this message."), _("Warning"));
+            if (!$inline) {
+                $phish_warning = '<span style="background-color:#ffd0af;color:black">' . String::convertCharset($phish_warning, NLS::getCharset(), $this->_mimepart->getCharset()) . '</span><br />';
+            }
+            $phish_warning = $this->formatStatusMsg($phish_warning, null, 'mimeStatusWarning');
+
+            $data = (stristr($data, '<body') === false)
+                ? $phish_warning . $data
+                : preg_replace('/(.*<body.*?>)(.*)/i', '$1' . $phish_warning . '$2', $data);
+        }
+
+        return $data;
+    }
+
+    /**
+     * TODO
+     */
+    protected function _dereferExternalReferencesCallback($m)
+    {
+        return 'href="' . Horde::externalUrl($m[2]) . '"';
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/images.php b/framework/mime_tmp/Horde/Mime/Viewer/images.php
new file mode 100644 (file)
index 0000000..ced92d7
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_images class allows images to be displayed.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_images extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Return the content-type.
+     *
+     * @return string  The content-type of the output.
+     */
+    public function getType()
+    {
+        $type = $this->mime_part->getType();
+        if ($GLOBALS['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.
+     *
+     * @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.
+     */
+    protected function _popupImageWindow($url, $title)
+    {
+        global $browser;
+
+        $str = <<<EOD
+<html>
+<head>
+<title>$title</title>
+<style type="text/css"><!-- body { margin:0px; padding:0px; } --></style>
+EOD;
+
+        /* Only use javascript if we are using a DOM capable browser. */
+        if ($browser->getFeature('dom')) {
+            /* Translate '&amp' entities to '&' for JS URL links. */
+            $url = str_replace('&amp;', '&', $url);
+
+            /* Javascript display. */
+            $loading = _("Loading...");
+            $str .= <<<EOD
+<script type="text/javascript">
+function resizeWindow()
+{
+
+    var h, img = document.getElementById('disp_image'), w;
+    document.getElementById('splash').style.display = 'none';
+    img.style.display = 'block';
+    window.moveTo(0, 0);
+    h = img.height - (window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.clientHeight));
+    w = img.width - (window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.clientWidth));
+    window.resizeBy(w, h);
+    self.focus();
+}
+</script></head>
+<body onload="resizeWindow();"><span id="splash" style="color:gray;font-family:sans-serif;padding:2px;">$loading</span><img id="disp_image" style="display:none;" src="$url" /></body></html>
+EOD;
+        } else {
+            /* Non-javascript display. */
+            $img_txt = _("Image");
+            $str .= <<<EOD
+</head>
+<body bgcolor="#ffffff" topmargin="0" marginheight="0" leftmargin="0" marginwidth="0">
+<img border="0" src="$url" alt="$img_txt" />
+</body>
+</html>
+EOD;
+        }
+
+        return $str;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/msexcel.php b/framework/mime_tmp/Horde/Mime/Viewer/msexcel.php
new file mode 100644 (file)
index 0000000..7b82a64
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_msexcel class renders out Microsoft Excel
+ * documents in HTML format by using the xlHtml package.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_msexcel extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the currently data using xlhtml.
+     *
+     * @param array $params  Any params this Viewer may need.
+     *
+     * @return string  The rendered data.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['msexcel']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['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.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/mspowerpoint.php b/framework/mime_tmp/Horde/Mime/Viewer/mspowerpoint.php
new file mode 100644 (file)
index 0000000..6bef4de
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_mspowerpoint class renders out Microsoft Powerpoint
+ * documents in HTML format by using the xlHtml package.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_mspowerpoint extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current data using ppthtml.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['mspowerpoint']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['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.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/msword.php b/framework/mime_tmp/Horde/Mime/Viewer/msword.php
new file mode 100644 (file)
index 0000000..7892fb2
--- /dev/null
@@ -0,0 +1,69 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_msword class renders out Microsoft Word documents
+ * in HTML format by using the wvWare package.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_msword extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current data using wvWare.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['msword']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['mime_drivers']['horde']['msword']['location']) . "\\wvHtml.xml -d $tmp_dir -1 $tmp_word > $tmp_output";
+        } else {
+            $version = exec($GLOBALS['mime_drivers']['horde']['msword']['location'] . ' --version');
+            if (version_compare($version, '0.7.0') >= 0) {
+                $args = " --charset=" . NLS::getCharset() . " --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($GLOBALS['mime_drivers']['horde']['msword']['location'] . $args);
+
+        if (!file_exists($tmp_output)) {
+            return _("Unable to translate this Word document");
+        }
+
+        return file_get_contents($tmp_output);
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo.php b/framework/mime_tmp/Horde/Mime/Viewer/ooo.php
new file mode 100644 (file)
index 0000000..4c82e2d
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_ooo class renders out OpenOffice.org documents in
+ * HTML format.
+ *
+ * Copyright 2003-2008 The Horde Project (http://www.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@oblo.com>
+ * @author  Jan Schneider <jan@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_ooo extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current data.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public 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(dirname(__FILE__) . '/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 String::convertCharset($xslt->result_dump_mem($result), 'UTF-8', NLS::getCharset());
+        } else {
+            // Use XSLT
+            $xslt = xslt_create();
+            $result = @xslt_process($xslt, $tmpdir . 'content.xml',
+                                    dirname(__FILE__) . '/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 String::convertCharset($result, 'UTF-8', NLS::getCharset());
+        }
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/common.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/common.xsl
new file mode 100644 (file)
index 0000000..943a5b9
--- /dev/null
@@ -0,0 +1,1165 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                extension-element-prefixes="xt"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+
+    <!-- ************ -->
+    <!-- *** body *** -->
+    <!-- ************ -->
+
+
+    <xsl:template match="/*/office:body">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- isDebugMode-START: only isDebugMode purpose: shows the inlined style attributes of the temporary variable -->
+        <xsl:if test="$isDebugMode and not($outputType = 'CSS_HEADER')">
+            <xsl:element name="debug_tree_of_styles"><xsl:text>
+            </xsl:text><xsl:for-each select="$collectedGlobalData/allstyles/*">
+<xsl:text>                      </xsl:text><xsl:value-of select="name()"/><xsl:text> = </xsl:text><xsl:value-of select="."/><xsl:text>
+            </xsl:text>
+                    </xsl:for-each>
+            </xsl:element>
+        </xsl:if>
+    <!-- isDebugMode-END -->
+
+
+               <!-- not using of 'apply-styles-and-content' as the content table information migth have been added to 'collectedGlobalData' variable -->
+        <xsl:apply-templates select="@text:style-name | @draw:style-name | @draw:text-style-name | @table:style-name"><!-- | @presentation:style-name -->
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+
+
+        <!-- Usability feature, a link to the Content talbe above all level 1 header -->
+        <xsl:if test="$contentTableHeadings">
+            <xsl:call-template name="add-child-document-usability-links"/>
+        </xsl:if>
+
+
+               <xsl:choose>
+               <xsl:when test="not($outputType = 'WML') and not($outputType = 'PALM')">
+                       <xsl:choose>
+                               <!--If the input document is a global document and embed child documents (links) the transformation of the children will be started as well.
+                                   This is necessary as child documents do not know anything about their embedding into a global document. Chapters of childs
+                                   always start to count by zero instead of continously numbering.
+                                   For this, the chapter numbers of the current document (as a sequence of a global document) is dependent
+                                   of the number of chapter of the same level in preceding documents.
+                                   In case of multiple children, for usability reasons some linking is gonna be offered and the URLs of the content-table,
+                                   preceding and following file have to be given for the transformation.
+                                   -->
+                           <xsl:when test="/*/@office:class='text-global' and /*/office:body/text:section/text:section-source/@xlink:href">
+                                               <!-- the children will be called later with a modified 'collectedGlobalData' variable -->
+                               <xsl:call-template name="transform-global-document-and-children">
+                                   <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                               </xsl:call-template>
+                           </xsl:when>
+                                       <xsl:otherwise>
+                                       <xsl:apply-templates>
+                                           <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                       </xsl:apply-templates>
+                                       </xsl:otherwise>
+                               </xsl:choose>
+               </xsl:when>
+                       <xsl:otherwise>
+                       <xsl:apply-templates>
+                           <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                       </xsl:apply-templates>
+                       </xsl:otherwise>
+               </xsl:choose>
+
+
+
+        <!-- Usability feature, a link to the Content talbe above all level 1 header -->
+        <xsl:if test="$contentTableHeadings">
+            <xsl:call-template name="add-child-document-usability-links"/>
+        </xsl:if>
+
+
+    </xsl:template>
+
+
+
+
+    <!-- deactivating default template -->
+    <xsl:template match="*"/>
+
+
+    <!-- allowing all matched text nodes -->
+    <xsl:template match="text()">
+<!-- WML         <xsl:value-of select="normalize-space(.)"/> -->
+        <xsl:value-of select="."/>
+    </xsl:template>
+
+
+
+    <!-- ################### -->
+    <!-- ### INLINE-TEXT ### -->
+    <!-- ################### -->
+
+
+    <!-- ****************** -->
+    <!-- *** Whitespace *** -->
+    <!-- ****************** -->
+
+
+    <xsl:template match="text:s">
+        <xsl:call-template name="write-breakable-whitespace">
+            <xsl:with-param name="whitespaces" select="@text:c"/>
+        </xsl:call-template>
+    </xsl:template>
+
+
+    <!--write the number of 'whitespaces' -->
+    <xsl:template name="write-breakable-whitespace">
+        <xsl:param name="whitespaces"/>
+
+        <!--write two space chars as the normal white space character will be stripped
+            and the other is able to break -->
+        <xsl:text>&#160;</xsl:text>
+        <xsl:if test="$whitespaces >= 2">
+            <xsl:call-template name="write-breakable-whitespace-2">
+                <xsl:with-param name="whitespaces" select="$whitespaces - 1"/>
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+
+    <!--write the number of 'whitespaces' -->
+    <xsl:template name="write-breakable-whitespace-2">
+        <xsl:param name="whitespaces"/>
+        <!--write two space chars as the normal white space character will be stripped
+            and the other is able to break -->
+        <xsl:text> </xsl:text>
+        <xsl:if test="$whitespaces >= 2">
+            <xsl:call-template name="write-breakable-whitespace">
+                <xsl:with-param name="whitespaces" select="$whitespaces - 1"/>
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+
+
+
+    <!-- *************** -->
+    <!-- *** Textbox *** -->
+    <!-- *************** -->
+
+    <xsl:template match="draw:text-box">
+        <xsl:param name="collectedGlobalData"/>
+
+            <xsl:choose>
+                <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+                <!--                or                  -->
+                <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'CSS_HEADER' or $outputType = 'CSS_INLINED'">
+                    <xsl:element name="span">
+                        <xsl:if test="@fo:min-height | @svg:width">
+                            <xsl:attribute name="style">
+                                <xsl:choose>
+                                    <xsl:when test="not(@svg:width)">
+                                        <xsl:text>height: </xsl:text><xsl:value-of select="@fo:min-height"/><xsl:text>; </xsl:text>
+                                    </xsl:when>
+                                    <xsl:when test="not(@fo:min-height)">
+                                        <xsl:text>width: </xsl:text><xsl:value-of select="@svg:width"/><xsl:text>; </xsl:text>
+                                    </xsl:when>
+                                    <xsl:otherwise>
+                                        <xsl:text>height: </xsl:text><xsl:value-of select="@fo:min-height"/><xsl:text>; </xsl:text>
+                                        <xsl:text>width: </xsl:text><xsl:value-of select="@svg:width"/><xsl:text>; </xsl:text>
+                                    </xsl:otherwise>
+                                </xsl:choose>
+                            </xsl:attribute>
+                        </xsl:if>
+                        <xsl:apply-templates select="@draw:name"/>
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!-- 2DO prove best usage for PALM -->
+                <!--+++++ PALM 3.2 SUBSET INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'PALM'">
+                    <xsl:element name="span">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!-- 2DO prove best usage for WML -->
+                <!--+++++ WML / WAP  +++++-->
+                <xsl:otherwise>
+                    <!-- no nested p tags in wml1.1 allowed -->
+                    <xsl:choose>
+                        <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                            <xsl:call-template name="apply-styles-and-content">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:element name="p">
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                             </xsl:element>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:otherwise>
+            </xsl:choose>
+    </xsl:template>
+
+    <!-- ID / NAME of text-box -->
+    <xsl:template match="@draw:name">
+
+        <xsl:attribute name="id">
+            <xsl:value-of select="."/>
+        </xsl:attribute>
+    </xsl:template>
+
+
+
+    <!-- ****************** -->
+    <!-- *** Paragraphs *** -->
+    <!-- ****************** -->
+
+    <xsl:template match="text:p | draw:page">
+        <xsl:param name="collectedGlobalData"/>
+
+            <xsl:choose>
+                <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+                <xsl:when test="$outputType = 'CSS_HEADER'">
+                    <xsl:element name="p">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+
+                <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'CSS_INLINED'">
+                    <xsl:element name="p">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!--+++++ PALM 3.2 SUBSET INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'PALM'">
+                    <xsl:choose>
+                        <!-- in palm paragraphs children of text:list-items are better shown without 'p' tag-->
+                        <xsl:when test="name(parent::*) = 'text:list-item'">
+                            <xsl:call-template name="apply-styles-and-content">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:element name="p">
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:when>
+                <!--+++++ WML / WAP  +++++-->
+                <xsl:otherwise>
+                    <!-- no nested p tags in wml1.1 allowed -->
+                    <xsl:choose>
+                        <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                            <xsl:call-template name="apply-styles-and-content">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:element name="p">
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                             </xsl:element>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:otherwise>
+            </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- ***************** -->
+    <!-- *** Text Span *** -->
+    <!-- ***************** -->
+
+    <xsl:template match="text:span">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+                <xsl:element name="span">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:element name="span">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <!--+++++ PALM 3.2 SUBSET INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'PALM'">
+                <xsl:call-template name="apply-styles-and-content">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <!--+++++ WML / WAP  +++++-->
+            <xsl:otherwise>
+                <!-- no nested p tags in wml1.1 allowed -->
+                <xsl:choose>
+                    <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:element name="p">
+                            <xsl:call-template name="apply-styles-and-content">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                        </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- **************** -->
+    <!-- *** Headings *** -->
+    <!-- **************** -->
+
+    <xsl:template match="text:h">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- Every heading element will get an unique anchor for its file, from its hiearchy level and name:
+            For example:  The heading title 'My favorite heading' might get <a name="1+2+2+My+favorite+heading"/> -->
+        <xsl:choose>
+            <xsl:when test="$disableLinkedTableOfContent or $isJavaDisabled or not($outputType = 'CSS_HEADER')">
+            <!-- The URL linking of an table-of-content is due to a bug (cmp. bug id# 102311) not mapped as URL in the XML.
+                 Linking of the table-of-content can therefore only be archieved by a work-around in HTML -->
+                <xsl:call-template name="create-heading">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+           </xsl:when>
+           <xsl:otherwise>
+                <!-- necessary as anchor for the content table -->
+                <xsl:call-template name="create-heading-anchor">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+
+                <!-- no embedding the orginal header, as an explicit anchor might be already set -->
+                <xsl:call-template name="create-heading">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- default matching for header elements -->
+    <xsl:template name="create-heading">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+
+                <xsl:variable name="headertyp" select="concat('h', @text:level)"/>
+                <xsl:element name="{$headertyp}">
+
+                    <!-- outline style 'text:min-label-width' is interpreted as a CSS 'margin-left' attribute -->
+                    <xsl:variable name="min-label" select="$office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/style:properties/@text:min-label-width"/>
+                    <xsl:if test="$min-label">
+                        <xsl:attribute name="style"><xsl:text>margin-left:</xsl:text><xsl:value-of select="$min-label"/><xsl:text>;</xsl:text></xsl:attribute>
+                    </xsl:if>
+
+
+                    <xsl:attribute name="class"><xsl:value-of select="translate(@text:style-name, '. %()/\', '')"/></xsl:attribute>
+
+                    <!-- writing out a chapter number if desired (noticable when a corresponding 'text:outline-style' exist -->
+                    <xsl:if test="string-length($office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/@style:num-format) != 0">
+
+                        <xsl:choose>
+                            <xsl:when test="$disableLinkedTableOfContent or $isJavaDisabled or not($outputType = 'CSS_HEADER')">
+                                <!-- the chapter number is the sum of 'text:start-value' and preceding siblings of 'text:h' with the same 'text:level',
+                                     furthermore when the current document is referenced by a global document - as part of a whole sequence of documents -,
+                                     the chapter no. is dependent of the amount of started headers in preceding documents.
+                                     If the 'text:start-value is not set the default value of '1' has to be taken. -->
+                                <xsl:variable name="startValue" select="$office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/@text:start-value"/>
+                                <xsl:choose>
+                                    <xsl:when test="$startValue">
+                                        <xsl:choose>
+                                            <xsl:when test="@text:level='1'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel1
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='2'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel2
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='3'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel3
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='4'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel4
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='5'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel5
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='6'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel6
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='7'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel7
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='8'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel8
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='9'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel9
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='10'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel10
+                                                            + $startValue"/>
+                                            </xsl:when>
+                                        </xsl:choose>
+                                    </xsl:when>
+                                    <xsl:otherwise>
+                                        <xsl:choose>
+                                            <xsl:when test="@text:level='1'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel1
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='2'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel2
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='3'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel3
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='4'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel4
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='5'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel5
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='6'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel6
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='7'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel7
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='8'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel8
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='9'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel9
+                                                            + 1"/>
+                                            </xsl:when>
+                                            <xsl:when test="@text:level='10'">
+                                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                            + $precedingChapterLevel10
+                                                            + 1"/>
+                                            </xsl:when>
+                                        </xsl:choose>
+                                    </xsl:otherwise>
+                                </xsl:choose>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:call-template name="get-absolute-chapter-no">
+                                    <xsl:with-param name="precedingChapterLevel1"    select="$precedingChapterLevel1"/>
+                                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                        <xsl:text> &#160; &#160;</xsl:text>
+                    </xsl:if>
+                    <xsl:apply-templates>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:apply-templates>
+
+                </xsl:element>
+            </xsl:when>
+
+
+            <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:variable name="headertyp" select="concat('h', @text:level)"/>
+                <xsl:element name="{$headertyp}">
+
+                    <xsl:apply-templates select="@text:style-name">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:apply-templates>
+
+                    <!-- writing out a chapter number if desired (noticable when a corresponding 'text:outline-style' exist -->
+                    <xsl:if test="$office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/@text:style-name">
+
+                        <!-- the chapter number is the sum of 'text:start-value' and preceding siblings of 'text:h' with the same 'text:level' -->
+                        <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                            + $office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/@text:start-value"/>
+                        <xsl:text> &#160; &#160;</xsl:text>
+                    </xsl:if>
+
+                </xsl:element>
+            </xsl:when>
+
+            <!-- 2DO: add Chapter No. for PALM and WML <-> problem nested apply-templates -->
+
+            <!--+++++ PALM 3.2 SUBSET INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'PALM'">
+                <xsl:variable name="headertyp" select="concat('h', @text:level)"/>
+                <xsl:element name="{$headertyp}">
+
+
+                    <!-- All children content have to be nested in the styles (e.g. <i><b>ANY CONTENT</b></i>)
+                         for this xsl:apply-templates will be called later / implicit -->
+                    <xsl:call-template name="create-attribute-ALIGN">
+                        <!-- getting the css styles for the style name (mapped by style-mapping.xsl) -->
+                        <xsl:with-param name="styleProperties" select="$collectedGlobalData/allstyles/*[name()=current()/@text:style-name]"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ WML / WAP  +++++-->
+            <xsl:otherwise>
+                <!-- no nested p tags in wml1.1 allowed -->
+                <xsl:choose>
+                    <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                        <!-- since no header styles exist, an emphasis is used -->
+                        <xsl:element name="em">
+
+                            <!-- writing out a chapter number if desired (noticable when a corresponding 'text:outline-style' exist -->
+                            <xsl:if test="$office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/@text:style-name">
+
+                                <!-- the chapter number is the sum of 'text:start-value' and preceding siblings of 'text:h' with the same 'text:level' -->
+                                <xsl:value-of select="count(preceding-sibling::text:h[@text:level = current()/@text:level])
+                                                    + $office:styles/text:outline-style/text:outline-level-style[@text:level = current()/@text:level]/@text:start-value"/>
+                                <xsl:text> &#160; &#160;</xsl:text>
+                            </xsl:if>
+
+                            <xsl:apply-templates select="@text:style-name">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:apply-templates>
+
+                        </xsl:element>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:element name="p">
+                            <!-- since no header styles exist, an emphasis is used -->
+                            <xsl:element name="em">
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                        </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- ************* -->
+    <!-- *** Link  *** -->
+    <!-- ************* -->
+
+    <xsl:template match="text:a | draw:a">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:call-template name="create-common-link">
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:call-template>
+    </xsl:template>
+
+
+    <xsl:template name="create-common-link">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <xsl:when test="not($outputType = 'WML')">
+                <xsl:element name="a">
+                    <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                    <!--<xsl:attribute name="class">ContentLink</xsl:attribute>-->
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- no nested p tags in wml1.1 allowed -->
+                <xsl:choose>
+                    <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                        <xsl:element name="a">
+                            <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                            <xsl:apply-templates select="descendant::text()"/>
+                        </xsl:element>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:element name="p">
+                            <xsl:element name="a">
+                                <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                                <xsl:apply-templates select="descendant::text()"/>
+                            </xsl:element>
+                        </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+
+    <!-- ******************* -->
+    <!-- *** Image Link  *** -->
+    <!-- ******************* -->
+
+    <xsl:template match="draw:image">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- NO IMAGES SUPPLIED FOR WAP OR PALM -->
+        <xsl:if test="$outputType = 'CSS_HEADER' or $outputType = 'CSS_INLINED'">
+
+            <xsl:element name="img">
+                <xsl:if test="@svg:width">
+                    <xsl:attribute name="width">
+                        <xsl:call-template name="convert2pixel">
+                            <xsl:with-param name="value" select="@svg:width"/>
+                        </xsl:call-template>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="@svg:height">
+                    <xsl:attribute name="height">
+                        <xsl:call-template name="convert2pixel">
+                            <xsl:with-param name="value" select="@svg:height"/>
+                        </xsl:call-template>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:if test="svg:desc">
+                    <xsl:attribute name="alt">
+                        <xsl:value-of select="svg:desc"/>
+                    </xsl:attribute>
+                </xsl:if>
+                <xsl:choose>
+                     <!-- for images jared in open office document -->
+                    <xsl:when test="contains(@xlink:href, '#Pictures/')">
+                        <!-- creating an absolute http URL to the packed image file -->
+                        <xsl:attribute name="src"><xsl:value-of select="concat($jaredRootURL, '/Pictures/', substring-after(@xlink:href, '#Pictures/'), $optionalURLSuffix)"/></xsl:attribute>
+                    </xsl:when>
+<!--                    Due to a XT bug no DOS ':' before DRIVE letter is allowed, it would result in a unkown protoco exception, but a file URL for a DOS
+                                               path needs the DRIVE letter, therefore all relative URLs remain relativ
+
+                                       <xsl:when test="contains(@xlink:href,'//') or (substring(@xlink:href,2,1) = ':') or starts-with(@xlink:href, '/')">
+                        <xsl:attribute name="src"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <!~~ creating a absolute path/URL for the referenced resource ~~>
+                        <xsl:attribute name="src"><xsl:value-of select="concat($absoluteSourceDirRef, @xlink:href, $optionalURLSuffix)"/></xsl:attribute>
+                    </xsl:otherwise>
+-->
+                                       <xsl:otherwise>
+                        <xsl:attribute name="src"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                    </xsl:otherwise>
+                </xsl:choose>
+
+                <xsl:call-template name="apply-styles-and-content">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:element>
+            <!-- adding a line break to make the presentation more even with the OOo view -->
+            <xsl:element name="br"/>
+        </xsl:if>
+    </xsl:template>
+
+
+
+    <!-- ******************** -->
+    <!-- *** ordered list *** -->
+    <!-- ******************** -->
+
+    <xsl:template match="text:ordered-list">
+        <xsl:param name="collectedGlobalData"/>
+
+            <xsl:choose>
+                <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+                <xsl:when test="$outputType = 'CSS_HEADER'">
+                    <xsl:element name="ol">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'CSS_INLINED'">
+                    <xsl:element name="ol">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!--+++++ PALM 3.2 SUBSET AND WAP INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'PALM'">
+                    <xsl:element name="ol">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!--+++++ WML / WAP  +++++-->
+                <xsl:otherwise>
+                    <xsl:choose>
+                        <!-- simulating content break of capsulated list elements -->
+                        <xsl:when test="ancestor::text:list-item">
+                            <xsl:choose>
+                                <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                                    <!-- simulating content break of capsulated list elements -->
+                                    <xsl:element name="br"></xsl:element>
+                                    <xsl:call-template name="apply-styles-and-content">
+                                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    </xsl:call-template>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:element name="p">
+                                        <!-- simulating content break of capsulated list elements -->
+                                        <xsl:element name="br"></xsl:element>
+                                        <xsl:call-template name="apply-styles-and-content">
+                                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                        </xsl:call-template>
+                                    </xsl:element>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:call-template name="apply-styles-and-content">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:otherwise>
+            </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- ********************** -->
+    <!-- *** unordered list *** -->
+    <!-- ********************** -->
+
+    <xsl:template match="text:unordered-list">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+                <xsl:element name="ul">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:element name="ul">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ PALM 3.2 SUBSET AND WAP INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'PALM'">
+                <xsl:element name="ul">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ WML / WAP  +++++-->
+            <xsl:otherwise>
+                <xsl:choose>
+                    <!-- simulating content break of capsulated list elements -->
+                    <xsl:when test="ancestor::text:list-item">
+                        <xsl:choose>
+                            <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                                <!-- simulating content break of capsulated list elements -->
+                                <xsl:element name="br"></xsl:element>
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:element name="p">
+                                    <!-- simulating content break of capsulated list elements -->
+                                    <xsl:element name="br"></xsl:element>
+                                    <xsl:call-template name="apply-styles-and-content">
+                                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    </xsl:call-template>
+                                </xsl:element>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- ****************** -->
+    <!-- *** list item  *** -->
+    <!-- ****************** -->
+
+    <xsl:template match="text:list-item">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+                <xsl:element name="li">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:element name="li">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ PALM 3.2 SUBSET AND WAP INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'PALM'">
+                <xsl:element name="li">
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ WML / WAP  +++++-->
+            <xsl:otherwise>
+                <xsl:choose>
+                    <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                        <!-- simulating list elements -->
+                        <xsl:for-each select="ancestor::text:list-item">*</xsl:for-each>
+                        <xsl:text>* </xsl:text>
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                        <!-- list item break simulation (not in a table)-->
+                        <xsl:if test="not(ancestor::table:table-cell) or following-sibling::text:list-item">
+                            <xsl:element name="br"/>
+                        </xsl:if>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:element name="p">
+                            <!-- simulating list elements -->
+                            <xsl:for-each select="ancestor::text:list-item">*</xsl:for-each>
+                            <xsl:text>* </xsl:text>
+                            <xsl:call-template name="apply-styles-and-content">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                            <!-- list item break simulation (not in a table)-->
+                            <xsl:if test="not(ancestor::table:table-cell) or following-sibling::text:list-item">
+                                <xsl:element name="br"/>
+                            </xsl:if>
+                        </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- ********************************************** -->
+    <!-- *** Text Section (contains: draw:text-box) *** -->
+    <!-- ********************************************** -->
+
+        <xsl:template match="text:section">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:if test="not(contains(@text:display, 'none'))">
+            <xsl:choose>
+                <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+                <xsl:when test="$outputType = 'CSS_HEADER'">
+                    <xsl:element name="span">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'CSS_INLINED'">
+                    <xsl:element name="span">
+                        <xsl:call-template name="apply-styles-and-content">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:element>
+                </xsl:when>
+                <!--+++++ PALM 3.2 SUBSET INLINED WAY  +++++-->
+                <xsl:when test="$outputType = 'PALM'">
+                    <xsl:choose>
+                        <xsl:when test="name(parent::*) = 'text:list-item'">
+                                        <xsl:call-template name="apply-styles-and-content">
+                                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                        </xsl:call-template>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:element name="p">
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:when>
+                <!--+++++ WML / WAP  +++++-->
+                <xsl:otherwise>
+                    <xsl:choose>
+                        <xsl:when test="not($outputType = 'WML')">
+                            <xsl:element name="a">
+                                <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                                <xsl:call-template name="apply-styles-and-content">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <!-- no nested p tags in wml1.1 allowed -->
+                            <xsl:choose>
+                                <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                                    <xsl:call-template name="apply-styles-and-content">
+                                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    </xsl:call-template>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:element name="p">
+                                        <xsl:call-template name="apply-styles-and-content">
+                                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                        </xsl:call-template>
+                                    </xsl:element>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:if>
+    </xsl:template>
+
+
+
+    <xsl:template match="text:line-break">
+        <xsl:element name="br"/>
+    </xsl:template>
+
+
+<!--
+    TABHANDLING PROBLEM: Tabs are possible to be shown in the HTML text file, but will be later stripped as whitespaces.
+        To prevent this one way would be the PRE tag which unfortunately ALWAYS result into a line-break. No surrounding NOBR tags help.
+
+    <xsl:template match="text:tab-stop">
+        <xsl:if test="not(preceding-sibling::text:tab-stop)">
+            <xsl:element name="pre"><xsl:text>&#9;</xsl:text><xsl:for-each select="following-sibling::text:tab-stop"><xsl:text>&#9;</xsl:text></xsl:for-each></xsl:element>
+        </xsl:if>
+     </xsl:template>
+
+    <xsl:template match="text:tab-stop"><xsl:text>&#9;</xsl:text></xsl:template>
+-->
+    <!-- HOTFIX: 8 non-breakable-spaces instead of a TAB is a hack sometimes less Tabs are needed and the code more difficult to read -->
+    <xsl:template match="text:tab-stop">
+        <xsl:call-template name="write-breakable-whitespace">
+            <xsl:with-param name="whitespaces" select="8"/>
+        </xsl:call-template>
+    </xsl:template>
+
+    <!-- currently there have to be an explicit call of the style attribute nodes, maybe the attributes nodes have no priority only order relevant-->
+    <!-- STRANGE: checked with biorythm.sxc a simple xsl:apply-templates did not recognice the styles. Maybe caused by the template match order?  -->
+    <xsl:template name="apply-styles-and-content">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:apply-templates select="@text:style-name | @draw:style-name | @draw:text-style-name | @table:style-name"><!-- | @presentation:style-name -->
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+
+        <xsl:apply-templates>
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+    </xsl:template>
+
+
+    <xsl:template match="@text:style-name | @draw:style-name | @draw:text-style-name | @table:style-name"><!-- | @presentation:style-name-->
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+                <xsl:attribute name="class"><xsl:value-of select="translate(., '. %()/\', '')"/></xsl:attribute>
+            </xsl:when>
+
+            <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:attribute name="style"><xsl:value-of select="$collectedGlobalData/allstyles/*[name()=current()/.]"/></xsl:attribute>
+            </xsl:when>
+
+            <!--+++++ PALM 3.2 SUBSET INLINED WAY  and  WML / WAP   +++++-->
+            <xsl:when test="$outputType = 'PALM' or $outputType = 'WML'">
+                <!-- getting the css styles for the style name (mapped by style-mapping.xsl) -->
+                <xsl:variable name="styleProperties" select="$collectedGlobalData/allstyles/*[name()=current()/.]"/>
+                <!-- changing the context node -->
+                <xsl:for-each select="parent::*">
+                    <xsl:call-template name="create-nested-format-tags">
+                        <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:for-each>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <xsl:template match="text:sequence">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:apply-templates>
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+    </xsl:template>
+
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/global_document.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/global_document.xsl
new file mode 100644 (file)
index 0000000..fc4579e
--- /dev/null
@@ -0,0 +1,1674 @@
+ <!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                extension-element-prefixes="xt"
+                xmlns:urlencoder="http://www.jclark.com/xt/java/java.net.URLEncoder"
+                xmlns:sxghelper="http://www.jclark.com/xt/java/com.sun.star.xslt.helper.SxgChildTransformer"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+
+
+    <!-- ********************************************** -->
+    <!-- *** Global Document -  Table of Content    *** -->
+    <!-- ********************************************** -->
+
+
+
+    <xsl:template match="text:table-of-content">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:apply-templates>
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+    </xsl:template>
+
+
+
+    <xsl:template match="text:index-body">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:apply-templates mode="content-table">
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+    </xsl:template>
+
+
+
+    <xsl:template match="text:index-title" mode="content-table">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:apply-templates>
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+    </xsl:template>
+
+    <xsl:template match="text:reference-ref">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- Java is needed as we have to encode the relative links (bug#102311) -->
+        <xsl:if test="not($isJavaDisabled)">
+            <xsl:element name="a">
+                <xsl:attribute name="href">
+                    <xsl:text>#</xsl:text>
+                    <xsl:call-template name="encode-string">
+                        <!-- the space has to be normalized,
+                            otherwise an illegal argument exception will be thrown for XT-->
+                         <xsl:with-param name="textToBeEncoded" select="@text:ref-name"/>
+                    </xsl:call-template>
+                </xsl:attribute>
+
+                <xsl:apply-templates>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:apply-templates>
+
+            </xsl:element>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="text:reference-mark">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- Java is needed as we have to encode the relative links (bug#102311) -->
+        <xsl:if test="not($isJavaDisabled)">
+            <xsl:element name="a">
+                <xsl:attribute name="name">
+                    <xsl:call-template name="encode-string">
+                        <!-- the space has to be normalized,
+                            otherwise an illegal argument exception will be thrown for XT-->
+                        <xsl:with-param name="textToBeEncoded" select="@text:name"/>
+                    </xsl:call-template>
+                </xsl:attribute>
+
+                <xsl:apply-templates>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:apply-templates>
+
+            </xsl:element>
+        </xsl:if>
+    </xsl:template>
+
+
+
+    <xsl:template match="text:reference-mark-start">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- Java is needed as we have to encode the relative links (bug#102311) -->
+        <xsl:if test="not($isJavaDisabled)">
+            <xsl:element name="a">
+                <xsl:attribute name="name">
+                    <xsl:call-template name="encode-string">
+                        <!-- the space has to be normalized,
+                            otherwise an illegal argument exception will be thrown for XT-->
+                        <xsl:with-param name="textToBeEncoded" select="@text:name"/>
+                    </xsl:call-template>
+                </xsl:attribute>
+
+                <xsl:variable name="endOfReference">
+                    <xsl:for-each select="text:reference-mark-end[@name=current()/@text:name]">
+                        <xsl:value-of select="position()"/>
+                    </xsl:for-each>
+                </xsl:variable>
+
+                <xsl:for-each select="following-sibling::*[position() &lt; $endOfReference]">
+                    <xsl:apply-templates>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:apply-templates>
+                </xsl:for-each>
+            </xsl:element>
+       </xsl:if>
+    </xsl:template>
+
+
+
+
+     <!-- content table link  -->
+    <xsl:template match="text:a" mode="content-table">
+        <xsl:param name="collectedGlobalData"/>
+
+
+        <!-- For anchors in content-headers a bug exists (cp. bug id# 102311) and they have to be worked out separately.
+            Currently the link used in the content-table of an Office XML (e.g. in the content table as '#7.Some%20Example%20Headline%7Outline')
+            is not a valid URL (cp. bug id# 102311). No file destination is specified nor exist any anchor element for these
+            links in the Office XML, nor is the chapter no. known in the linked files.
+            A workaround for this transformation therefore had to be made. This time-consuming mechanism is disabled by default and
+            can be activated by a parameter (i.e. 'disableLinkedTableOfContent'). A creation of an anchor is made for each header element.
+            All header titles gonna be encoding to be usable in a relative URL. -->
+        <xsl:choose>
+            <xsl:when test="$disableLinkedTableOfContent or $isJavaDisabled">
+                <xsl:call-template name="create-common-link">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:call-template name="create-content-table-link">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <xsl:template name="get-absolute-chapter-no">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="precedingChapterLevel1"/>
+
+        <xsl:choose>
+            <xsl:when test="$globalDocumentRefToCurrentFile">
+
+                <xsl:variable name="currentFileHeadingNo">
+                    <xsl:call-template name="get-current-file-heading-no"/>
+                </xsl:variable>
+                <xsl:variable name="testResult" select="$contentTableHeadings/heading[$globalDocumentRefToCurrentFile = @file-url][number($currentFileHeadingNo)]"/>
+
+                <xsl:call-template name="get-global-heading-no">
+                    <xsl:with-param name="currentFileHeadingNo" select="translate($testResult/@absolute-chapter-level, '+', '.')"/>
+                    <xsl:with-param name="precedingChapterLevel1" select="$precedingChapterLevel1"/>
+                </xsl:call-template>
+
+           </xsl:when>
+           <xsl:otherwise>
+                <!-- When the chapter is in the global document itself the link has to be relative (e.g. #index) a absolute href does not
+                    work with the browser. In case of chapter in the global document, the output URL of the global document was taken. -->
+                <xsl:variable name="currentFileHeadingNo">
+                    <xsl:call-template name="get-current-file-heading-no"/>
+                </xsl:variable>
+                <xsl:variable name="testResult" select="$collectedGlobalData/content-table-headings/heading[$contentTableURL = @file-url][number($currentFileHeadingNo)]"/>
+
+                <xsl:call-template name="get-global-heading-no">
+                    <xsl:with-param name="currentFileHeadingNo" select="translate($testResult/@absolute-chapter-level, '+', '.')"/>
+                    <xsl:with-param name="precedingChapterLevel1" select="$precedingChapterLevel1"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <xsl:template name="get-current-file-heading-no">
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-current-child-heading-no')">
+                <xsl:value-of select="sxghelper:get-current-child-heading-no()"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getCurrentChildHeadingNo')">
+                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.getCurrentChildHeadingNo()"/>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <xsl:template name="get-next-current-file-heading-no">
+        <xsl:param name="file"/>
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-next-current-child-heading-no')">
+                <xsl:value-of select="sxghelper:get-next-current-child-heading-no($file)"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getNextCurrentChildHeadingNo')">
+                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.getNextCurrentChildHeadingNo($file)"/>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <xsl:template name="get-global-heading-no">
+        <xsl:param name="currentFileHeadingNo"/>
+        <xsl:param name="precedingChapterLevel1"/>
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-global-heading-no')">
+                <xsl:value-of select="sxghelper:get-global-heading-no(string($currentFileHeadingNo), number($precedingChapterLevel1))"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getGlobalHeadingNo')">
+                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.getGlobalHeadingNo(string($currentFileHeadingNo), number($precedingChapterLevel1))"/>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+    <!-- necessary as anchor for the content table -->
+    <xsl:template name="create-heading-anchor">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!--
+        Currently the link used in the Office XML (e.g. in the content table as '#7.Some%20Example%20Headline%7Outline')
+        is not a valid URL (cmp. bug id# 102311). No file destination is specified nor exist any anchor element for these
+        links in the Office XML.
+        Here we are creating an anchor with the space normalized text of this header as potential jump address of the content table -->
+
+        <xsl:choose>
+            <xsl:when test="$globalDocumentRefToCurrentFile">
+
+                <xsl:variable name="currentFileHeadingNo">
+                    <xsl:call-template name="get-next-current-file-heading-no">
+                         <xsl:with-param name="file" select="$globalDocumentRefToCurrentFile"/>
+                    </xsl:call-template>
+                </xsl:variable>
+
+
+                <xsl:variable name="testResult" select="$contentTableHeadings/heading[$globalDocumentRefToCurrentFile = @file-url][number($currentFileHeadingNo)]"/>
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>Matching child document header No. <xsl:value-of select="$currentFileHeadingNo"/></xsl:message>
+                    <xsl:message>absolute-chapter-level:         <xsl:value-of select="$testResult/@absolute-chapter-level"/></xsl:message>
+                    <xsl:message>encodedTitle:                   <xsl:value-of select="$testResult/@encoded-title"/></xsl:message>
+                    <xsl:message>globalDocumentRefToCurrentFile: <xsl:value-of select="$globalDocumentRefToCurrentFile"/></xsl:message>
+                    <xsl:message>*** </xsl:message>
+                </xsl:if>
+
+                <xsl:element name="a">
+                    <xsl:attribute name="name">
+                        <xsl:value-of select="$testResult/@absolute-chapter-level"/>
+                        <xsl:text>+</xsl:text>
+                        <xsl:value-of select="$testResult/@encoded-title"/>
+                    </xsl:attribute>
+                </xsl:element>
+           </xsl:when>
+
+           <xsl:otherwise>
+                <!-- When the chapter is in the global document itself the link has to be relative (e.g. #index) a absolute href does not
+                    work with the browser. In case of chapter in the global document, the output URL of the global document was taken. -->
+                <xsl:variable name="currentFileHeadingNo">
+                    <xsl:call-template name="get-next-current-file-heading-no">
+                         <xsl:with-param name="file" select="$contentTableURL"/>
+                    </xsl:call-template>
+                </xsl:variable>
+
+
+                <xsl:variable name="testResult" select="$collectedGlobalData/content-table-headings/heading[$contentTableURL = @file-url][number($currentFileHeadingNo)]"/>
+
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>Matching global document header No. <xsl:value-of select="$currentFileHeadingNo"/></xsl:message>
+                    <xsl:message>absolute-chapter-level:  <xsl:value-of select="$testResult/@absolute-chapter-level"/></xsl:message>
+                    <xsl:message>encodedTitle:            <xsl:value-of select="$testResult/@encoded-title"/></xsl:message>
+                    <xsl:message>contentTableURL:         <xsl:value-of select="$contentTableURL"/></xsl:message>
+                    <xsl:message>*** </xsl:message>
+                </xsl:if>
+
+                <xsl:element name="a">
+                    <xsl:attribute name="name">
+                        <xsl:value-of select="$testResult/@absolute-chapter-level"/>
+                        <xsl:text>+</xsl:text>
+                        <xsl:value-of select="$testResult/@encoded-title"/>
+                    </xsl:attribute>
+                </xsl:element>
+
+            </xsl:otherwise>
+        </xsl:choose>
+
+
+
+<!--
+
+        <xsl:variable name="title" select="normalize-space(.)"/>
+        <!~~DON'T WORK    <xsl:variable name="title" select="normalize-space(descendant::text())"/>        ~~>
+         <xsl:choose>
+            <xsl:when test="$globalDocumentRefToCurrentFile">
+                <xsl:variable name="testResults" select="$contentTableHeadings/heading[$globalDocumentRefToCurrentFile = @file-url][$title = @title][current()/@text:level = @level]"/>
+                <xsl:if test="1 &lt; count($testResults)">
+                    <xsl:message> *** CAUTION: Multiple chapter headings with similar names: </xsl:message>
+                    <xsl:message> *** Title: <xsl:value-of select="$title"/> Level: <xsl:value-of select="@text:level"/></xsl:message>
+                </xsl:if>
+
+                 <xsl:variable name="encodedTitle" select="$testResults/@encoded-title"/>
+                 <xsl:choose>
+                     <xsl:when test="$encodedTitle">
+                         <xsl:element name="a">
+                            <xsl:attribute name="name">
+                                <xsl:value-of select="$encodedTitle"/>
+                            </xsl:attribute>
+                         </xsl:element>
+                     </xsl:when>
+                     <xsl:otherwise>
+                        <!~~ even when it is not ~~>
+                        <xsl:variable name="newEncodedTitle">
+                            <xsl:call-template name="encode-string">
+                                <!~~ the space has to be normalized,
+                                    otherwise an illegal argument exception will be thrown for XT~~>
+                                 <xsl:with-param name="textToBeEncoded" select="$title"/>
+                            </xsl:call-template>
+                        </xsl:variable>
+                         <xsl:element name="a">
+                            <xsl:attribute name="name">
+                                <xsl:value-of select="$newEncodedTitle"/>
+                            </xsl:attribute>
+                         </xsl:element>
+                     </xsl:otherwise>
+                 </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:variable name="testResults" select="$collectedGlobalData/content-table-headings/heading[$contentTableURL = @file-url][$title = @title][current()/@text:level = @level]"/>
+                <xsl:if test="1 &lt; count($testResults)">
+                    <xsl:message> *** CAUTION: Multiple chapter headings with similar names: </xsl:message>
+                    <xsl:message> *** Title: <xsl:value-of select="$title"/> Level: <xsl:value-of select="@text:level"/></xsl:message>
+                    <xsl:message> *** </xsl:message>
+                </xsl:if>
+
+                <xsl:variable name="encodedTitle" select="$testResults/@encoded-title"/>
+                <xsl:choose>
+                     <xsl:when test="$encodedTitle">
+                         <xsl:element name="a">
+                            <xsl:attribute name="name">
+                                <xsl:value-of select="$encodedTitle"/>
+                            </xsl:attribute>
+                         </xsl:element>
+                     </xsl:when>
+                     <xsl:otherwise>
+                        <!~~ even when it is not ~~>
+                        <xsl:variable name="newEncodedTitle">
+                            <xsl:call-template name="encode-string">
+                                <!~~ the space has to be normalized,
+                                    otherwise an illegal argument exception will be thrown for XT~~>
+                                 <xsl:with-param name="textToBeEncoded" select="$title"/>
+                            </xsl:call-template>
+                        </xsl:variable>
+                         <xsl:element name="a">
+                            <xsl:attribute name="name">
+                                <xsl:value-of select="$newEncodedTitle"/>
+                            </xsl:attribute>
+                         </xsl:element>
+                     </xsl:otherwise>
+                 </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+
+-->
+
+    </xsl:template>
+
+
+
+
+    <!-- ************************************** -->
+    <!--    CREATION OF A CONTENT TABLE LINK    -->
+    <!-- ************************************** -->
+
+
+    <!-- a special behavior of text:a
+        (called from the 'text:a' template) -->
+
+    <xsl:template name="create-content-table-link">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <xsl:when test="not($outputType = 'WML')">
+                <xsl:element name="a">
+                    <xsl:attribute name="href">
+                        <xsl:choose>
+                            <xsl:when test="starts-with(@xlink:href, '#')">
+                                <xsl:variable name="correctHeading" select="$collectedGlobalData/content-table-headings/heading[current()/@xlink:href = @content-table-id]"/>
+
+                                <xsl:value-of select="$correctHeading/@out-file-url"/>
+                                <xsl:text>#</xsl:text>
+                                <xsl:value-of select="$correctHeading/@absolute-chapter-level"/>
+                                <xsl:text>+</xsl:text>
+                                <xsl:value-of select="$correctHeading/@encoded-title"/>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:call-template name="create-common-link">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+
+                    <xsl:call-template name="apply-styles-and-content">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- 2DO: currently no WML support
+
+                <!~~ no nested p tags in wml1.1 allowed ~~>
+                <xsl:choose>
+                    <xsl:when test="ancestor::*[contains($wap-paragraph-elements, name())]">
+                        <xsl:element name="a">
+                            <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                            <xsl:apply-templates select="."/>
+                        </xsl:element>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:element name="p">
+                            <xsl:element name="a">
+                                <xsl:attribute name="href"><xsl:value-of select="@xlink:href"/></xsl:attribute>
+                                <xsl:apply-templates select="."/>
+                            </xsl:element>
+                        </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>  -->
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+       <!--
+           CREATION OF A HELPER VARIABLE AS WORKAROUND FOR THE CONTENT TABLE ULR BUG
+
+
+        As no valid URL from the content table to the child documents exist in the content table,
+        a work-around is done:
+
+        First two helper variables are being created.
+
+        One containing the list of all references of the global document:
+        containg all their title,
+        for example:
+
+               <chapter-ref title="aTitle 1"/>
+               <chapter-ref title="aTitle 2"/>
+               <chapter-ref title="aTitle 2/>
+               <chapter-ref title="aTitle 3/>
+
+        The other containing all heading from the child documents linked from the global document.
+        The variable 'childrenHeadings' contains their title and the number of preceding similar titles,
+        for example:
+
+
+               <child file-url="aURL">
+                       <heading title="aTitle1" level="1">
+                       <heading title="aTitle2" level="2">
+                       <heading title="aTitle3" level="1">
+            </child>
+
+        For each chapter reference from the content table the
+
+         by encoding the chapter names of the child document with the java URLEncoder and
+        use this as a part of a link. Furthermore for all heading elements a encoded anchor will be created from the heading.
+        Last the workaround parses all children documents for this anhor, as there is no distinction of files from the content table entries.
+
+        The new added node set to the collectedGlobalData variable concering the content table is written as
+
+
+               <content-table-headings content-table-url="aURL_ToTheGeneratedContentTable">
+               <heading file-url="aFileURLToTheGeneratedHeading1" level="1">
+               <heading file-url="aFileURLToTheGeneratedHeading2" level="2">
+               <heading file-url="aFileURLToTheGeneratedHeading1" level="1">
+               <heading file-url="aFileURLToTheGeneratedHeading2" level="2">
+               </content-table-headings>
+
+
+        Preconditions:
+               The correct sequence of child documents according to the Content Table is necessary, granted by the office.
+       -->
+       <xsl:template name="Create-helper-variables-for-Content-Table">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:if test="$isDebugMode"><xsl:message>Creation of global document helper variable for the content table....</xsl:message></xsl:if>
+
+        <!-- Here a helper variable of the content table is created, of all chapter-references which point to a child document.
+             an 'chapter-ref' element will be created, containg their title and the number of preceding similar titles,
+             for example:
+
+               <chapter-ref title="aTitle 1"/>
+               <chapter-ref title="aTitle 2"/>
+               <chapter-ref title="aTitle 2"/>
+               <chapter-ref title="aTitle 3"/>
+            -->
+        <xsl:variable name="chapterRefs-RTF">
+            <!-- '/*/' as the flat and the zipped XML file format have different root elements -->
+            <xsl:for-each select="/*/office:body/text:table-of-content/text:index-body/text:p/text:a">
+                <xsl:variable name="currentTitle" select="normalize-space(string(.))"/>
+                <xsl:element name="chapter-ref">
+                    <xsl:attribute name="title">
+                        <xsl:value-of select="$currentTitle"/>
+                    </xsl:attribute>
+                    <xsl:attribute name="content-table-id">
+                        <xsl:value-of select="@xlink:href"/>
+                    </xsl:attribute>
+               </xsl:element>
+            </xsl:for-each>
+        </xsl:variable>
+        <xsl:if test="$isDebugMode"><xsl:message>Finished the Creation of global document helper variable for the content table!</xsl:message></xsl:if>
+
+
+
+
+        <xsl:if test="$isDebugMode"><xsl:message>Creation of global document helper variable for the child documents....</xsl:message></xsl:if>
+        <!-- Here a helper variable of created from the children documents.
+             Containg all heading elements from the child documents. Some or all of them are
+             chapters referenced by the Global Document.
+             The variable contains their title, the level of the heading and the file URL of the child,
+             for example:
+
+                       <heading title="aTitle1" level="1" file-url="aURL1">
+                       <heading title="aTitle2" level="2" file-url="aURL1">
+                       <heading title="aTitle3" level="1" file-url="aURL1">
+                       <heading title="aTitle4" level="1" file-url="aURL2">
+                       <heading title="aTitle5" level="2" file-url="aURL2">
+                       <heading title="aTitle2" level="3" file-url="aURL2">
+                       <heading title="aTitle6" level="3" file-url="aURL2">
+                <heading-count>7</heading-count>
+            -->
+        <xsl:variable name="childrenHeadings-RTF">
+            <!-- all headers from children documents will be added -->
+            <xsl:apply-templates select="/*/office:body/text:section" mode="creation-of-variable"/>
+        </xsl:variable>
+        <xsl:if test="$isDebugMode"><xsl:message>Finished the Creation of global document helper variable for the child documents!</xsl:message></xsl:if>
+
+
+        <xsl:choose>
+            <xsl:when test="function-available('xt:node-set')">
+                <xsl:call-template name="Create-global-variable-for-Content-Table">
+                    <xsl:with-param name="chapterRefs"     select="xt:node-set($chapterRefs-RTF)"/>
+                    <xsl:with-param name="childrenHeadings"     select="xt:node-set($childrenHeadings-RTF)"/>
+                    <xsl:with-param name="collectedGlobalData"  select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('xalan:nodeset')">
+                <xsl:call-template name="Create-global-variable-for-Content-Table">
+                    <xsl:with-param name="chapterRefs"     select="xalan:nodeset($chapterRefs-RTF)"/>
+                    <xsl:with-param name="childrenHeadings"     select="xalan:nodeset($childrenHeadings-RTF)"/>
+                    <xsl:with-param name="collectedGlobalData"  select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+    <xsl:template name="Create-global-variable-for-Content-Table">
+        <xsl:param name="chapterRefs"/>
+        <xsl:param name="childrenHeadings"/>
+        <xsl:param name="collectedGlobalData"/>
+
+
+        <xsl:if test="$isDebugMode">
+            <!-- helper variable collecting all headings from the global document file children-->
+            <xsl:for-each select="$childrenHeadings/heading">
+                <xsl:message>#              <xsl:value-of select="position()"/></xsl:message>
+                <xsl:message>level:         <xsl:value-of select="@level"/></xsl:message>
+                <xsl:message>title:         <xsl:value-of select="@title"/></xsl:message>
+                <xsl:message>encoded-title: <xsl:value-of select="@encoded-title"/></xsl:message>
+                <xsl:message>file-url:      <xsl:value-of select="@file-url"/></xsl:message>
+                <xsl:message>header-no:     <xsl:value-of select="@header-no"/></xsl:message>
+                <xsl:message>**</xsl:message>
+            </xsl:for-each>
+            <xsl:message>**</xsl:message>
+            <xsl:message>**</xsl:message>
+
+            <!-- helper variable collecting all heading references from the content table of the the global document -->
+            <xsl:message>childrenHeadings/heading-count: <xsl:value-of select="$childrenHeadings/heading-count"/></xsl:message>
+            <xsl:for-each select="$chapterRefs/chapter-ref">
+                <xsl:message># <xsl:value-of select="position()"/></xsl:message>
+                <xsl:message>title: <xsl:value-of select="@title"/></xsl:message>
+                <xsl:message>**</xsl:message>
+            </xsl:for-each>
+        </xsl:if>
+
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:set-heading-no')">
+                    <xsl:value-of select="sxghelper:set-heading-no(1)"/>
+                    <xsl:value-of select="sxghelper:set-current-child-no(1)"/>
+                    <xsl:value-of select="sxghelper:set-current-child-url(string($childrenHeadings/heading/@file-url))"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.setHeadingNo')">
+                    <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.setHeadingNo(1)"/>
+                    <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.setCurrentChildNo(1)"/>
+                    <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.setCurrentChildUrl(string($childrenHeadings/heading/@file-ur))"/>
+            </xsl:when>
+        </xsl:choose>
+
+        <xsl:if test="$isDebugMode"><xsl:message>Creating global document variable for chapter relations....</xsl:message></xsl:if>
+        <xsl:variable name="contentTableHeadingsGlobalData-RTF">
+            <xsl:element name="content-table-headings">
+                <!-- all headings are linked from the current global document input file -->
+                <xsl:attribute name="content-table-url">
+                    <xsl:value-of select="$contentTableURL"/>
+                </xsl:attribute>
+
+                <!-- had to use a for loop, as a recursion ends with an stackoverflow exception after about 600 recursive calls -->
+                <xsl:choose>
+                    <xsl:when test="function-available('sxghelper:get-heading-no')">
+                         <xsl:for-each select="$chapterRefs/chapter-ref">
+                            <xsl:call-template name="searchHeadingInChildDocument">
+                                <xsl:with-param name="chapterRefs"         select="$chapterRefs"/>
+                                <xsl:with-param name="childrenHeadings"    select="$childrenHeadings"/>
+                                <xsl:with-param name="currentChapterRefNo" select="position()"/>
+                                <xsl:with-param name="currentHeadingNo"    select="sxghelper:get-heading-no()"/>
+                                <xsl:with-param name="currentChildURL"     select="sxghelper:get-current-child-url()"/>
+                                <xsl:with-param name="currentChildNo"      select="sxghelper:get-current-child-no()"/>
+                            </xsl:call-template>
+                        </xsl:for-each>
+                     </xsl:when>
+                    <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getHeadingNo')">
+                         <xsl:for-each select="$chapterRefs/chapter-ref">
+                            <xsl:call-template name="searchHeadingInChildDocument">
+                                <xsl:with-param name="chapterRefs"         select="$chapterRefs"/>
+                                <xsl:with-param name="childrenHeadings"    select="$childrenHeadings"/>
+                                <xsl:with-param name="currentChapterRefNo" select="position()"/>
+                                <xsl:with-param name="currentHeadingNo"    select="java:com.sun.star.xslt.helper.SxgChildTransformer.getHeadingNo()"/>
+                                <xsl:with-param name="currentChildURL"     select="java:com.sun.star.xslt.helper.SxgChildTransformer.getCurrentChildUrl()"/>
+                                <xsl:with-param name="currentChildNo"      select="java:com.sun.star.xslt.helper.SxgChildTransformer.getCurrentChildNo()"/>
+                            </xsl:call-template>
+                        </xsl:for-each>
+                    </xsl:when>
+                </xsl:choose>
+            </xsl:element>
+
+            <!-- adding the already exisiting global data environment -->
+            <xsl:copy-of select="$collectedGlobalData"/>
+        </xsl:variable>
+        <xsl:if test="$isDebugMode"><xsl:message>Finished global document variable for chapter relations!</xsl:message></xsl:if>
+
+        <xsl:choose>
+            <xsl:when test="function-available('xt:node-set')">
+                <xsl:call-template name="start-self-and-children-transformation">
+                    <xsl:with-param name="collectedGlobalData"       select="xt:node-set($contentTableHeadingsGlobalData-RTF)"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('xalan:nodeset')">
+                <xsl:call-template name="start-self-and-children-transformation">
+                    <xsl:with-param name="collectedGlobalData"       select="xalan:nodeset($contentTableHeadingsGlobalData-RTF)"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <xsl:template name="searchHeadingInChildDocument">
+        <xsl:param name="chapterRefs"/>
+        <xsl:param name="childrenHeadings"/>
+        <xsl:param name="currentChapterRefNo"/>
+        <xsl:param name="currentHeadingNo"/>
+        <xsl:param name="currentChildURL"/>
+        <xsl:param name="currentChildNo"/>
+
+
+        <xsl:variable name="currentChapterRef"      select="$chapterRefs/chapter-ref[$currentChapterRefNo]"/>
+        <xsl:variable name="currentChapterID"       select="$currentChapterRef/@content-table-id"/>
+        <xsl:variable name="currentChapterTitle"    select="$currentChapterRef/@title"/>
+
+        <xsl:variable name="currentChildHeading"    select="$childrenHeadings/heading[$currentHeadingNo]"/>
+        <xsl:variable name="headingTitle"           select="$currentChildHeading/@title"/>
+        <xsl:variable name="headingLevel"           select="$currentChildHeading/@level"/>
+        <xsl:variable name="headingNo"              select="$currentChildHeading/@header-no"/>
+        <xsl:variable name="newChildURL"            select="$currentChildHeading/@file-url"/>
+
+        <xsl:if test="$isDebugMode">
+            <xsl:message>*** new heading </xsl:message>
+            <xsl:message>currentChapterID:    <xsl:value-of select="$currentChapterID"/></xsl:message>
+            <xsl:message>currentChapterTitle: <xsl:value-of select="$currentChapterTitle"/></xsl:message>
+            <xsl:message>currentChapterID:    <xsl:value-of select="$currentChapterID"/></xsl:message>
+            <xsl:message>currentHeadingNo:    <xsl:value-of select="$currentHeadingNo"/></xsl:message>
+            <xsl:message>headingTitle:        <xsl:value-of select="$headingTitle"/></xsl:message>
+            <xsl:message>headingLevel:        <xsl:value-of select="$headingLevel"/></xsl:message>
+            <xsl:message>headingNo:           <xsl:value-of select="$headingNo"/></xsl:message>
+            <xsl:message>newChildURL:         <xsl:value-of select="$newChildURL"/></xsl:message>
+        </xsl:if>
+
+        <xsl:variable name="outFileURL">
+            <xsl:choose>
+                 <xsl:when test="substring-before($newChildURL,'.xml')">
+                    <xsl:value-of select="concat(substring-before($newChildURL,'.xml'),'.htm')"/>
+                 </xsl:when>
+                 <xsl:when test="substring-before($newChildURL,'.sx')">
+                    <xsl:value-of select="concat(substring-before($newChildURL,'.sx'),'.htm')"/>
+                 </xsl:when>
+            </xsl:choose>
+        </xsl:variable>
+        <xsl:variable name="isChapterHeading" select="$headingTitle = $currentChapterTitle"/>
+        <xsl:variable name="isNewFile" select="string($newChildURL) != string($currentChildURL)"/>
+
+
+
+
+        <xsl:if test="$isNewFile">
+            <!-- reset of the already collected child headers -->
+            <xsl:call-template name="calc-chapter-numbers">
+                 <xsl:with-param name="level" select="0"/>
+            </xsl:call-template>
+        </xsl:if>
+        <xsl:variable name="absoluteChapterLevel">
+            <xsl:call-template name="calc-chapter-numbers">
+                 <xsl:with-param name="level" select="number($headingLevel)"/>
+            </xsl:call-template>
+        </xsl:variable>
+
+
+        <xsl:element name="heading">
+            <!-- necessary to as ID from the content table to get the correct heading element (the buggy URL used as ID)-->
+            <xsl:attribute name="content-table-id">
+                <xsl:choose>
+                    <xsl:when test="$isChapterHeading">
+                        <xsl:value-of select="$currentChapterID"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:text>only a heading, but not a chapter</xsl:text>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <!-- no of the used child, necessary for quick finding of chapters of next file  -->
+            <xsl:attribute name="child-document-no">
+                <xsl:choose>
+                    <xsl:when test="$isNewFile">
+                        <xsl:value-of select="$currentChildNo + 1"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:value-of select="$currentChildNo"/>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <!-- the URL of the child document source, containing the heading -->
+            <xsl:attribute name="file-url">
+                <xsl:value-of select="$newChildURL"/>
+            </xsl:attribute>
+            <xsl:attribute name="out-file-url">
+                <xsl:value-of select="$outFileURL"/>
+            </xsl:attribute>
+            <xsl:attribute name="level">
+                <xsl:value-of select="$headingLevel"/>
+            </xsl:attribute>
+            <xsl:attribute name="title">
+                <xsl:value-of select="$headingTitle"/>
+            </xsl:attribute>
+            <xsl:attribute name="encoded-title">
+                <xsl:value-of select="$currentChildHeading/@encoded-title"/>
+            </xsl:attribute>
+            <xsl:attribute name="absolute-chapter-level">
+                <xsl:value-of select="$absoluteChapterLevel"/>
+            </xsl:attribute>
+        </xsl:element>
+
+
+        <xsl:choose>
+            <xsl:when test="$childrenHeadings/heading-count != $currentHeadingNo">
+                <!-- procede as long the list of children isn'nt worked through -->
+                <xsl:choose>
+                    <xsl:when test="$isChapterHeading">
+                        <!-- global variables have to be set, so the for-each loop can access them -->
+                        <xsl:choose>
+                            <xsl:when test="function-available('sxghelper:set-heading-no')">
+                                <xsl:value-of select="sxghelper:set-heading-no($currentHeadingNo + 1)"/>
+                                <xsl:if test="$isNewFile">
+                                    <xsl:value-of select="sxghelper:set-current-child-no($currentChildNo + 1)"/>
+                                    <xsl:value-of select="sxghelper:set-current-child-url(string($newChildURL))"/>
+                                </xsl:if>
+                             </xsl:when>
+                            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.setHeadingNo')">
+                                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.setHeadingNo($currentHeadingNo + 1)"/>
+                                <xsl:if test="$isNewFile">
+                                    <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.setCurrentChildNo($currentChildNo + 1)"/>
+                                    <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.setCurrentChildUrl($newChildURL)"/>
+                                </xsl:if>
+                            </xsl:when>
+                        </xsl:choose>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <!-- not a chapter heading, call itself until a chapter ref is found or the end of headings is reached -->
+                        <xsl:call-template name="searchHeadingInChildDocument">
+                            <xsl:with-param name="chapterRefs"         select="$chapterRefs"/>
+                            <xsl:with-param name="childrenHeadings"    select="$childrenHeadings"/>
+                            <xsl:with-param name="currentChapterRefNo" select="$currentChapterRefNo"/>
+                            <xsl:with-param name="currentHeadingNo"    select="$currentHeadingNo + 1"/>
+                            <xsl:with-param name="currentChildURL"     select="$currentChildURL"/>
+                            <xsl:with-param name="currentChildNo"      select="$currentChildNo"/>
+                        </xsl:call-template>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>All child documents have been walked through without finding the chapter name!</xsl:message>
+                    <xsl:message>       childrenHeadings/heading-count:    <xsl:value-of select="$childrenHeadings/heading-count"/></xsl:message>
+                    <xsl:message>       currentHeadingNo:                  <xsl:value-of select="$currentHeadingNo"/></xsl:message>
+                </xsl:if>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+
+       <!-- Chapters from the Content Table have currently no anchor to child documents in OOo XML.
+                As solution, whenever a a global document  every header of the HTML output gets get's an anchor in the Therefore-->
+    <xsl:template name="encode-string">
+        <xsl:param name="encoding" select="'UTF-8'"/>
+        <xsl:param name="textToBeEncoded"/>
+
+        <xsl:choose>
+            <xsl:when test="function-available('urlencoder:encode')">
+                <xsl:value-of select="urlencoder:encode(normalize-space($textToBeEncoded),$encoding)"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:java.net.URLEncoder.encode')">
+                <xsl:value-of select="java:java.net.URLEncoder.encode(string(normalize-space($textToBeEncoded)),string($encoding))"/>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+
+    <!-- ******************************************************************************************************** -->
+    <!-- ***  TRANSFORMATION OF ALL CHILD DOCUMENTS OF THE GLOBAL DOCUMENTS BY USING A EXTERNAL HELPER CLASS  *** -->
+    <!-- ******************************************************************************************************** -->
+
+
+       <!-- a new element 'contentTableHeadings' will be added to the helper variable the first time a child will be transformed -->
+    <xsl:template name="transform-global-document-and-children">
+        <xsl:param name="collectedGlobalData"/>
+
+
+        <xsl:choose>
+            <xsl:when test="$collectedGlobalData/content-table-headings">
+                <xsl:call-template name="start-child-transformation">
+                    <xsl:with-param name="collectedGlobalData"   select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                   <!-- The necessary auxiliary variable hasn't build yet.
+                   This variable gonna store all headers (with chapter numbers) and the URL of their files -->
+
+                <xsl:call-template name="Create-helper-variables-for-Content-Table">
+                    <xsl:with-param name="collectedGlobalData"   select="$collectedGlobalData"/>
+                </xsl:call-template>
+
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+       <xsl:template name="start-self-and-children-transformation">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:if test="$isDebugMode">
+            <xsl:call-template name="debug-content-table-headings-variable">
+                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+            </xsl:call-template>
+
+            <xsl:message>Parsing the global document...</xsl:message>
+        </xsl:if>
+
+        <xsl:apply-templates>
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:apply-templates>
+
+
+        <xsl:if test="$isDebugMode"><xsl:message>Parsing the child documents...</xsl:message></xsl:if>
+        <xsl:call-template name="start-child-transformation">
+            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+        </xsl:call-template>
+
+       </xsl:template>
+
+
+
+
+    <xsl:template name="start-child-transformation">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:if test="$isDebugMode"><xsl:message>Starting the child transformations...</xsl:message></xsl:if>
+
+        <!-- As the childs of a global document (with suffix .sxg) do not know anything about their global parent,
+            the transformation of global documents children have to be done implizit.
+            Otherwise the chapter number of the children will always start with zero, as they do not know anything about the
+            proceding chapters.
+            Furthermore, they don't have any links about preceeding and following documents and no linking for usability reasons
+            could be done. Therefore the children have to be transformed during the transformation of a global (sxg) document -->
+               <xsl:if test="$isDebugMode">
+            <xsl:choose>
+                <xsl:when test="$collectedGlobalData/content-table-headings">
+                    <xsl:message>Contentable data exists as global data!</xsl:message>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:message>No Contentable global data exists!</xsl:message>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:if>
+
+        <!-- currently this function only works with node-sets from XT -->
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:transform-children')">
+                <xsl:message>
+                    <xsl:value-of select="sxghelper:transform-children( $collectedGlobalData/content-table-headings,
+                                                                        string($jaredRootURL),
+                                                                        string($absoluteSourceDirRef),
+                                                                        string($optionalURLSuffix),
+                                                                        string($dpi),
+                                                                        string($outputType),
+                                                                        $isDebugMode)"/>
+                </xsl:message>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:message>Java method transformChildren to transform all children of a global document could not be found. Be sure to use the XT processor.</xsl:message>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+
+    <!-- ******************************************************************************* -->
+    <!-- ***  Creation of helper variable of the headings of all children documents  *** -->
+    <!-- ******************************************************************************* -->
+
+
+    <xsl:template match="/*/office:body/text:section" mode="creation-of-variable">
+        <xsl:call-template name="getChildRootNode"/>
+
+        <!-- after the last child document the global document will be parsed -->
+        <xsl:if test="position() = last()">
+            <!-- search the global document after all child documents have been searched
+
+ODK PATCH NO INDEX ELEMENT WANTED !! - null pointer exception
+            <xsl:call-template name="getPreviousHeaderNo">
+                <xsl:with-param name="fileURL"                  select="$contentTableURL"/>
+                <xsl:with-param name="amountOfCurrentHeading"   select="count(following-sibling::text:h)"/>
+                <xsl:with-param name="nodeToSearchForHeading"   select="following-sibling::text:h"/>
+            </xsl:call-template>
+-->
+           <!-- get the overall No of Headers -->
+           <xsl:call-template name="getAllHeaderNo"/>
+        </xsl:if>
+    </xsl:template>
+
+
+    <xsl:template name="getChildRootNode">
+        <xsl:variable name="fileURL"    select="text:section-source/@xlink:href"/>
+
+        <xsl:choose>
+               <!-- if absolute URL or absolute DOS PATH or absolute Unix path -->
+                       <xsl:when test="contains($fileURL,'//') or (substring($fileURL,2,1) = ':') or starts-with($fileURL, '/')">
+                       <xsl:variable name="childRootNode" select="document($fileURL)"/>
+                <xsl:call-template name="getPreviousHeaderNo">
+                    <xsl:with-param name="fileURL"                select="$fileURL"/>
+                       <!-- NO absolute source path will be added as prefix -->
+                    <xsl:with-param name="amountOfCurrentHeading" select="count($childRootNode/*/office:body/descendant::text:h)"/>
+                    <xsl:with-param name="nodeToSearchForHeading" select="$childRootNode/*/office:body/descendant::text:h"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                       <xsl:variable name="childRootNode" select="document(concat($absoluteSourceDirRef,'/',$fileURL))"/>
+                <xsl:call-template name="getPreviousHeaderNo">
+                    <xsl:with-param name="fileURL"                select="$fileURL"/>
+                       <!-- the absolute source path will be added as prefix -->
+                    <xsl:with-param name="amountOfCurrentHeading" select="count($childRootNode/*/office:body/descendant::text:h)"/>
+                    <xsl:with-param name="nodeToSearchForHeading" select="$childRootNode/*/office:body/descendant::text:h"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <xsl:template name="getPreviousHeaderNo">
+        <xsl:param name="fileURL"/>
+        <xsl:param name="nodeToSearchForHeading"/>
+        <xsl:param name="amountOfCurrentHeading"/>
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-previous-child-documents-heading-count')">
+                <xsl:call-template name="addHeadingInfo">
+                    <xsl:with-param name="nodeToSearchForHeading"   select="$nodeToSearchForHeading"/>
+                    <xsl:with-param name="fileURL"                  select="$fileURL"/>
+                    <xsl:with-param name="previousHeader"           select="sxghelper:get-previous-child-documents-heading-count($amountOfCurrentHeading)"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getPreviousChildDocumentsHeadingCount')">
+                <xsl:call-template name="addHeadingInfo">
+                    <xsl:with-param name="nodeToSearchForHeading"   select="$nodeToSearchForHeading"/>
+                    <xsl:with-param name="fileURL"                  select="$fileURL"/>
+                    <xsl:with-param name="previousHeader"           select="java:com.sun.star.xslt.helper.SxgChildTransformer.getPreviousChildDocumentsHeadingCount($amountOfCurrentHeading)"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+
+    </xsl:template>
+
+
+    <xsl:template name="addHeadingInfo">
+        <xsl:param name="fileURL"/>
+        <xsl:param name="previousHeader"/>
+        <xsl:param name="nodeToSearchForHeading"/>
+
+        <xsl:variable name="previousHeader2" select="number($previousHeader)"/>
+        <xsl:for-each select="$nodeToSearchForHeading">
+
+            <xsl:variable name="title" select="normalize-space(.)"/>
+
+            <xsl:variable name="encodedTitle">
+                <xsl:call-template name="encode-string">
+                    <!-- the space has to be normalized,
+                        otherwise an illegal argument exception will be thrown for XT-->
+                     <xsl:with-param name="textToBeEncoded" select="$title"/>
+                </xsl:call-template>
+            </xsl:variable>
+
+            <xsl:element name="heading">
+                <!-- odd but 'descendant:text()' didn't work, but '.', to get all text nodes of the header -->
+                <xsl:attribute name="title"><xsl:value-of select="$title"/></xsl:attribute>
+                <xsl:attribute name="encoded-title"><xsl:value-of select="$encodedTitle"/></xsl:attribute>
+                <xsl:attribute name="level"><xsl:value-of select="@text:level"/></xsl:attribute>
+                <xsl:attribute name="file-url"><xsl:value-of select="$fileURL"/></xsl:attribute>
+                <xsl:attribute name="header-no"><xsl:value-of select="position() + $previousHeader2"/></xsl:attribute>
+            </xsl:element>
+        </xsl:for-each>
+
+    </xsl:template>
+
+
+    <xsl:template name="getAllHeaderNo">
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-all-child-documents-heading-count')">
+                <xsl:call-template name="addAllHeaderNoElement">
+                    <xsl:with-param name="allHeader"   select="sxghelper:get-all-child-documents-heading-count()"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getAllChildDocumentsHeadingCount')">
+                <xsl:call-template name="addAllHeaderNoElement">
+                    <xsl:with-param name="allHeader"   select="java:com.sun.star.xslt.helper.SxgChildTransformer.getAllChildDocumentsHeadingCount()"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template name="addAllHeaderNoElement">
+        <xsl:param name="allHeader"/>
+
+        <xsl:element name="heading-count">
+            <xsl:value-of select="$allHeader"/>
+        </xsl:element>
+
+    </xsl:template>
+
+
+    <!-- ******************************************************************************************************* -->
+    <!-- ***  Creation of a line of links at the beginning and end of a child document to enhance usability  *** -->
+    <!-- ******************************************************************************************************* -->
+
+    <xsl:template name="add-child-document-usability-links">
+        <xsl:element name="center">
+            <xsl:element name="small">
+                <xsl:text>[ </xsl:text>
+
+
+                <xsl:variable name="globalDocumentDir" select="sxghelper:get-global-document-dir()"/>
+                <xsl:variable name="currentChildNo" select="number($contentTableHeadings/heading[$globalDocumentRefToCurrentFile = @file-url]/@child-document-no)"/>
+                <xsl:variable name="earlierDocURL" select="$contentTableHeadings/heading[($currentChildNo - 1) = @child-document-no]/@out-file-url"/>
+<!--
+<xsl:message>from: <xsl:value-of select="$globalDocumentRefToCurrentFile"/></xsl:message>
+<xsl:message>to: <xsl:value-of select="$earlierDocURL"/></xsl:message>
+<xsl:message>Is: <xsl:call-template name="get-relative-file-ref">
+                    <xsl:with-param name="sourceFileRef" select="$globalDocumentRefToCurrentFile"/>
+                    <xsl:with-param name="targetFileRef" select="$earlierDocURL"/>
+                 </xsl:call-template>
+</xsl:message>-->
+
+
+                <xsl:if test="$earlierDocURL">
+                    <xsl:element name="a">
+                        <xsl:attribute name="href">
+                            <!-- when the links starts with a '#' it's a link to the Content Table-->
+                            <xsl:choose>
+                                <xsl:when test="starts-with($earlierDocURL, '#')">
+
+                                    <xsl:call-template name="get-relative-file-ref">
+                                        <xsl:with-param name="sourceFileRef" select="$globalDocumentRefToCurrentFile"/>
+                                        <xsl:with-param name="targetFileRef" select="."/>
+                                    </xsl:call-template>
+<!--        <xsl:value-of select="concat($contentTableURL, $earlierDocURL)"/>-->
+                                </xsl:when>
+                                <xsl:otherwise>
+
+                                    <xsl:call-template name="get-relative-file-ref">
+                                        <xsl:with-param name="sourceFileRef" select="$globalDocumentRefToCurrentFile"/>
+                                        <xsl:with-param name="targetFileRef" select="$earlierDocURL"/>
+                                    </xsl:call-template>
+<!--
+
+                                    <xsl:value-of select="concat($globalDocumentDir, $earlierDocURL)"/>-->
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:attribute>
+                        <xsl:text>Previous document</xsl:text>
+                    </xsl:element>
+
+                    <xsl:text> | </xsl:text>
+                </xsl:if>
+
+                <xsl:element name="a">
+                    <xsl:attribute name="href">
+                        <!-- when globalDocumentRefToCurrentFile is unset the current file is the Content Table-->
+                        <xsl:choose>
+                            <xsl:when test="$globalDocumentRefToCurrentFile">
+                                <xsl:variable name="contentTableDir">
+                                    <xsl:call-template name="get-name-of-table-of-content-document"/>
+                                </xsl:variable>
+
+                                <xsl:call-template name="get-relative-file-ref">
+                                    <xsl:with-param name="sourceFileRef" select="$globalDocumentRefToCurrentFile"/>
+                                    <xsl:with-param name="targetFileRef" select="$contentTableDir"/>
+                                </xsl:call-template>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:text>#</xsl:text>
+                            </xsl:otherwise>
+                        </xsl:choose>
+
+<!--                    <xsl:value-of select="$contentTableURL"/>-->
+                    </xsl:attribute>
+                    <xsl:text>Content Table</xsl:text>
+                </xsl:element>
+
+
+                <xsl:variable name="nextDocURL" select="$contentTableHeadings/heading[($currentChildNo + 1) = @child-document-no]/@out-file-url"/>
+                <xsl:if test="$nextDocURL">
+                    <xsl:text> | </xsl:text>
+                    <xsl:element name="a">
+                        <xsl:attribute name="href">
+                            <!-- when the links starts with a '#' it's a link to the Content Table-->
+                            <xsl:choose>
+                                <xsl:when test="starts-with($nextDocURL, '#')">
+                                    <xsl:call-template name="get-relative-file-ref">
+                                        <xsl:with-param name="sourceFileRef" select="$globalDocumentRefToCurrentFile"/>
+                                        <xsl:with-param name="targetFileRef" select="."/>
+                                    </xsl:call-template>
+<!--                                <xsl:value-of select="concat($contentTableURL, $nextDocURL)"/>-->
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:call-template name="get-relative-file-ref">
+                                        <xsl:with-param name="sourceFileRef" select="$globalDocumentRefToCurrentFile"/>
+                                        <xsl:with-param name="targetFileRef" select="$nextDocURL"/>
+                                    </xsl:call-template>
+<!--                                 <xsl:value-of select="concat($globalDocumentDir, $nextDocURL)"/>-->
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:attribute>
+                        <xsl:text>Next document</xsl:text>
+                    </xsl:element>
+                </xsl:if>
+                <xsl:text> ]</xsl:text>
+            </xsl:element>
+        </xsl:element>
+    </xsl:template>
+
+
+    <xsl:template name="get-relative-file-ref">
+        <xsl:param name="sourceFileRef"/>
+        <xsl:param name="targetFileRef"/>
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-relative-file-ref')">
+                <xsl:value-of select="sxghelper:get-relative-file-ref(string($sourceFileRef), string($targetFileRef))"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getRelativeFileRef')">
+                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.getRelativeFileRef(string($sourceFileRef), string($targetFileRef))"/>
+            </xsl:when>
+        </xsl:choose>
+
+    </xsl:template>
+
+
+    <xsl:template name="get-name-of-table-of-content-document">
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:get-name-of-table-of-content-document')">
+                <xsl:value-of select="sxghelper:get-name-of-table-of-content-document()"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.getNameOfTableOfContentDocument')">
+                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.getNameOfTableOfContentDocument()"/>
+            </xsl:when>
+        </xsl:choose>
+
+    </xsl:template>
+
+
+       <xsl:template name="debug-content-table-headings-variable">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:message><xsl:text>**** THE HEADING VARIABLE **** </xsl:text></xsl:message>
+        <xsl:message>content-table-url: <xsl:value-of select="collectedGlobalData/content-table-headings/content-table-url"/></xsl:message>
+
+        <xsl:for-each select="$collectedGlobalData/content-table-headings/heading">
+            <xsl:message><xsl:text>**** new heading:        </xsl:text></xsl:message>
+            <xsl:message>content-table-id:      <xsl:value-of select="@content-table-id"/></xsl:message>
+            <xsl:message>child-document-no:     <xsl:value-of select="@child-document-no"/></xsl:message>
+            <xsl:message>file-url:              <xsl:value-of select="@file-url"/></xsl:message>
+            <xsl:message>out-file-url:          <xsl:value-of select="@out-file-url"/></xsl:message>
+            <xsl:message>level:                 <xsl:value-of select="@level"/></xsl:message>
+            <xsl:message>title:                 <xsl:value-of select="@title"/></xsl:message>
+            <xsl:message>encoded-title:         <xsl:value-of select="@encoded-title"/></xsl:message>
+            <xsl:message>absolute-chapter-level:<xsl:value-of select="@absolute-chapter-level"/></xsl:message>
+        </xsl:for-each>
+
+       </xsl:template>
+
+
+       <!-- To make the headings unique, the absolute heading is added to them
+            E.g. The level 1.2.3.4. would result into a 1+2+3+4 string -->
+    <xsl:template name="calc-chapter-numbers">
+        <xsl:param name="level"/>
+
+        <xsl:choose>
+            <xsl:when test="function-available('sxghelper:calc-chapter-numbers')">
+                <xsl:value-of select="sxghelper:calc-chapter-numbers($level)"/>
+            </xsl:when>
+            <xsl:when test="function-available('java:com.sun.star.xslt.helper.SxgChildTransformer.calcChapterNumbers')">
+                <xsl:value-of select="java:com.sun.star.xslt.helper.SxgChildTransformer.calcChapterNumbers($level)"/>
+            </xsl:when>
+        </xsl:choose>
+
+    </xsl:template>
+
+
+
+
+    <xsl:template match="text:p" mode="content-table">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:variable name="allTabStopStyles" select="$office:automatic-styles/style:style[@style:name = current()/@text:style-name]/style:properties/style:tab-stops"/>
+
+        <xsl:element name="table">
+            <xsl:attribute name="border">0</xsl:attribute>
+            <xsl:attribute name="class"><xsl:value-of select="@text:style-name"/></xsl:attribute>
+<!--
+<xsl:message>*********</xsl:message>
+<xsl:message>Stylename:<xsl:value-of select="@text:style-name"/></xsl:message>
+<xsl:message>position: <xsl:value-of select="count($allTabStopStyles/style:tab-stop)"/></xsl:message>
+-->
+
+            <xsl:element name="colgroup">
+                <xsl:call-template name="create-col-element">
+                    <xsl:with-param name="lastNodePosition" select="count($allTabStopStyles/style:tab-stop)"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                </xsl:call-template>
+            </xsl:element>
+
+
+            <!-- all elements before the first tabStop -->
+            <xsl:variable name="testNo-RTF">
+                <xsl:apply-templates select="node()" mode="cell-content"/>
+            </xsl:variable>
+
+
+        <xsl:choose>
+            <xsl:when test="function-available('xt:node-set')">
+                <xsl:variable name="tabNodePositions" select="xt:node-set($testNo-RTF)"/>
+                       <xsl:element name="tr">
+                        <xsl:call-template name="create-td-elements">
+                    <xsl:with-param name="lastNodePosition" select="count($allTabStopStyles/style:tab-stop)"/>
+                    <xsl:with-param name="position"         select="1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                    <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:element>
+
+            </xsl:when>
+            <xsl:when test="function-available('xalan:nodeset')">
+                <xsl:variable name="tabNodePositions" select="xalan:nodeset($testNo-RTF)"/>
+                       <xsl:element name="tr">
+                        <xsl:call-template name="create-td-elements">
+                    <xsl:with-param name="lastNodePosition" select="count($allTabStopStyles/style:tab-stop)"/>
+                    <xsl:with-param name="position"         select="1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                    <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:element>
+
+            </xsl:when>
+        </xsl:choose>
+
+           <!-- <xsl:variable name="tabNodePositions" select="xt:node-set($testNo-RTF)"/>
+
+            <xsl:element name="tr">
+                <xsl:call-template name="create-td-elements">
+                    <xsl:with-param name="lastNodePosition" select="count($allTabStopStyles/style:tab-stop)"/>
+                    <xsl:with-param name="position"         select="1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                    <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:element>-->
+
+
+        </xsl:element>
+    </xsl:template>
+
+
+    <xsl:template name="create-col-element">
+        <xsl:param name="lastNodePosition"/>
+        <xsl:param name="allTabStopStyles"/>
+
+        <xsl:for-each select="$allTabStopStyles/style:tab-stop">
+            <xsl:element name="col">
+                <xsl:attribute name="style">
+                    <xsl:text>width: </xsl:text>
+                    <xsl:call-template name="grap-cell-width">
+                        <xsl:with-param name="position"         select="position()"/>
+                        <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                    </xsl:call-template>
+                </xsl:attribute>
+            </xsl:element>
+        </xsl:for-each>
+
+    </xsl:template>
+<!--
+Scenarios tabstops
+
+1) style:type of style:tab-stop is 'right' and earlier tabStop is not right
+ -> Earlier text-nodes and following text-nodes, will be put into an inner table, with two TD first aligned left, with proceding textnodes, the latter aligned right.
+
+2) style:type is 'right' and earlier tabStop is right
+ -> following text-nodes, will be put into a right aligned TD
+
+3) style:type is 'non-right' and earlier tabStop 'non-right' as well
+ -> put the preceding tab stops into a TD (left aligned is default)
+
+4) first style:type would have no right precedign tabStop
+ -> works well with first sceanrios 1 and 3
+
+5) last style:type would be a special case, if it would be left aligned, but this won't happen in our case.. :D
+
+Scenarios unmatched:
+- text:styleposition 'center' will not be matched in our case (effort for nothing), there will be only 'right' and not 'right'
+- If the last tabStop is not from text:stylepostion 'right', the length of the last cell is undefined and a document length must be found.
+  Not happens in our global document case. Also the algorithm below would have to be expanded (cp. scenario 5).
+
+-->
+    <xsl:template name="create-td-elements">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="lastNodePosition"/>
+        <xsl:param name="position"/>
+        <xsl:param name="allTabStopStyles"/>
+        <xsl:param name="tabNodePositions"/>
+<!--
+<xsl:message>++++++++</xsl:message>
+<xsl:message>Position: <xsl:value-of select="$position"/></xsl:message>
+<xsl:message>lastNodePosition: <xsl:value-of select="$lastNodePosition"/></xsl:message>
+-->
+
+        <xsl:variable name="currentStyleType" select="$allTabStopStyles/style:tab-stop[$position]/@style:type"/>
+        <xsl:variable name="earlierStyleType" select="$allTabStopStyles/style:tab-stop[$position - 1]/@style:type"/>
+        <xsl:choose>
+            <xsl:when test="$currentStyleType = 'right'">
+                <xsl:choose>
+                    <xsl:when test="$earlierStyleType = 'right'">
+                        <!--
+                        2) style:type is 'right' and earlier tabStop is right
+                            -> following text-nodes, will be put into a right aligned TD -->
+                        <xsl:element name="td">
+                            <xsl:attribute name="style">
+                                <xsl:text>align: right</xsl:text>
+                            </xsl:attribute>
+                            <xsl:call-template name="grap-cell-content-before-tab-stop">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                <xsl:with-param name="endingTabStopPosition"  select="$position + 1"/>
+                                <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                                <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                            </xsl:call-template>
+                        </xsl:element>
+                    </xsl:when>
+                    <xsl:otherwise>
+                    <!--
+                        1) style:type of style:tab-stop is 'right' and earlier tabStop is not right
+                         -> Earlier text-nodes and following text-nodes, will be put into an inner table, with two TD first aligned left, with proceding textnodes, the latter aligned right.-->
+<!-- valid HTML but browsers make a line break (border=0 and paragraphstyle also missing):
+                        <xsl:element name="table">
+                            <xsl:element name="td">
+                                <xsl:call-template name="grap-cell-content-before-tab-stop">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    <xsl:with-param name="endingTabStopPosition"  select="$position"/>
+                                    <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                                    <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                            <xsl:element name="td">
+                                <xsl:attribute name="style">
+                                    <xsl:text>align: right</xsl:text>
+                                </xsl:attribute>
+                                <xsl:call-template name="grap-cell-content-before-tab-stop">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    <xsl:with-param name="endingTabStopPosition"  select="$position + 1"/>
+                                    <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                                    <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                        </xsl:element>
+-->
+                            <xsl:element name="td">
+                                <xsl:call-template name="grap-cell-content-before-tab-stop">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    <xsl:with-param name="endingTabStopPosition"  select="$position"/>
+                                    <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                                    <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                                </xsl:call-template>
+<!-- ODK FEATURE NO PAGES
+                                <xsl:element name="td">
+                                    <xsl:attribute name="style">
+                                        <xsl:text>align: right</xsl:text>
+                                    </xsl:attribute>
+                                    <xsl:call-template name="grap-cell-content-before-tab-stop">
+                                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                        <xsl:with-param name="endingTabStopPosition"  select="$position + 1"/>
+                                        <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                                        <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                                    </xsl:call-template>
+                                </xsl:element>          -->
+                            </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:choose>
+                    <xsl:when test="$earlierStyleType = 'right'">
+                    </xsl:when>
+                    <xsl:otherwise>
+                    <!--
+                       3) style:type is 'non-right' and earlier tabStop 'non-right' as well
+                            -> put the preceding tab stops into a TD (left aligned is default) -->
+                        <xsl:element name="td">
+                            <xsl:call-template name="grap-cell-content-before-tab-stop">
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                <xsl:with-param name="endingTabStopPosition"  select="$position"/>
+                                <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                                <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+                            </xsl:call-template>
+                        </xsl:element>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+
+        <xsl:if test="$position != $lastNodePosition">
+            <xsl:call-template name="create-td-elements">
+                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                <xsl:with-param name="lastNodePosition" select="$lastNodePosition"/>
+                <xsl:with-param name="position"         select="$position + 1"/>
+                <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                <xsl:with-param name="tabNodePositions" select="$tabNodePositions"/>
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+
+    <xsl:template name="grap-cell-content-before-tab-stop">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="endingTabStopPosition"/>
+        <xsl:param name="tabNodePositions"/>
+        <xsl:param name="lastNodePosition"/>
+
+        <xsl:choose>
+            <xsl:when test="$endingTabStopPosition = 1">
+                <xsl:apply-templates mode="content-table" select="node()[position() &lt; $tabNodePositions/tab-stop-node-position[$endingTabStopPosition]]">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:apply-templates>
+            </xsl:when>
+            <xsl:when test="$endingTabStopPosition > $lastNodePosition">
+                <xsl:apply-templates mode="content-table" select="node()[position() > $tabNodePositions/tab-stop-node-position[$endingTabStopPosition - 1]]">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:apply-templates>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:apply-templates mode="content-table" select="node()[position() &lt; $tabNodePositions/tab-stop-node-position[$endingTabStopPosition]][position() > $tabNodePositions/tab-stop-node-position[$endingTabStopPosition - 1]]">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:apply-templates>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template mode="content-table" match="text:s">
+        <xsl:call-template name="write-breakable-whitespace">
+            <xsl:with-param name="whitespaces" select="@text:c"/>
+        </xsl:call-template>
+    </xsl:template>
+
+
+    <xsl:template match="*" mode="cell-content">
+
+        <xsl:if test="name() = 'text:tab-stop' or *[name() = 'text:tab-stop']">
+            <xsl:element name="tab-stop-node-position">
+                <xsl:value-of select="position()"/>
+            </xsl:element>
+        </xsl:if>
+    </xsl:template>
+
+
+    <xsl:template name="grap-cell-width">
+        <xsl:param name="position"/>
+        <xsl:param name="allTabStopStyles"/>
+
+        <xsl:variable name="tabStopPosition" select="$allTabStopStyles/style:tab-stop[$position]/@style:position"/>
+        <xsl:choose>
+            <xsl:when test="contains($tabStopPosition, 'cm')">
+                <xsl:call-template name="create-cell-width">
+                    <xsl:with-param name="width"    select="number(substring-before($tabStopPosition,'cm'))"/>
+                    <xsl:with-param name="unit"     select="'cm'"/>
+                    <xsl:with-param name="position" select="$position - 1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="contains($tabStopPosition, 'in')">
+                <xsl:call-template name="create-cell-width">
+                    <xsl:with-param name="width"    select="number(substring-before($tabStopPosition,'in'))"/>
+                    <xsl:with-param name="unit"     select="'in'"/>
+                    <xsl:with-param name="position" select="$position - 1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="contains($tabStopPosition, 'ch')">
+                <xsl:call-template name="create-cell-width">
+                    <xsl:with-param name="width"    select="number(substring-before($tabStopPosition,'ch'))"/>
+                    <xsl:with-param name="unit"     select="'ch'"/>
+                    <xsl:with-param name="position" select="$position - 1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="contains($tabStopPosition, 'pt')">
+                <xsl:call-template name="create-cell-width">
+                    <xsl:with-param name="width"    select="number(substring-before($tabStopPosition,'pt'))"/>
+                    <xsl:with-param name="unit"     select="'pt'"/>
+                    <xsl:with-param name="position" select="$position - 1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template name="create-cell-width">
+        <xsl:param name="width"/>
+        <xsl:param name="unit"/>
+        <xsl:param name="position"/>
+        <xsl:param name="allTabStopStyles"/>
+
+        <xsl:choose>
+            <xsl:when test="$position > 1">
+                <xsl:call-template name="create-cell-width">
+                    <xsl:with-param name="width"    select="$width - number(substring-before($allTabStopStyles/style:tab-stop[$position]/@style:position,$unit))"/>
+                    <xsl:with-param name="unit"     select="$unit"/>
+                    <xsl:with-param name="position" select="$position - 1"/>
+                    <xsl:with-param name="allTabStopStyles" select="$allTabStopStyles"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="$position = 1">
+                <xsl:value-of select="concat($width - number(substring-before($allTabStopStyles/style:tab-stop[$position]/@style:position,$unit)), $unit)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="concat($width, $unit)"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/main_html.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/main_html.xsl
new file mode 100644 (file)
index 0000000..443182a
--- /dev/null
@@ -0,0 +1,462 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:urlencoder="http://www.jclark.com/xt/java/java.net.URLEncoder"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+    <xsl:output method      ="xml"
+                encoding    ="UTF-8"
+                indent      ="yes"/>
+
+
+
+    <!--+++++ INCLUDED XSL MODULES +++++-->
+    <!-- inherited style properties will be collected and written in a CSS header (CSS) -->
+    <xsl:include href="style_header.xsl"/>
+
+    <!-- inherited style properties will be collected and written as html properties in a temporary variable (HTML4, PALM) -->
+    <xsl:include href="style_inlined.xsl"/>
+
+    <!-- our xml style properties will be mapped to CSS and HTML4.x properties -->
+    <xsl:include href="style_mapping.xsl"/>
+
+    <!-- common element handling -->
+    <xsl:include href="common.xsl"/>
+
+    <!-- table handling -->
+    <xsl:include href="table.xsl"/>
+
+    <!-- palm handling -->
+    <xsl:include href="palm.xsl"/>
+
+    <!-- global document handling -->
+    <xsl:include href="global_document.xsl"/>
+
+
+
+
+
+
+
+    <!--+++++ PARAMETER FROM THE APPLICATION AND GLOBAL VARIABLES +++++-->
+
+    <!-- MANDATORY: URL of meta stream -->
+    <xsl:param name="metaFileURL"/>
+
+    <!-- MANDATORY: URL of styles stream -->
+    <xsl:param name="stylesFileURL"/>
+
+    <!-- MANDATORY: for resolving relative links
+        For resolving realtive links to the packed SO document, i.e. the path/URL of the jared sxw file (e.g. meta.xml, styles.xml, links to graphics in a relative directory) -->
+    <xsl:param name="absoluteSourceDirRef"/>
+
+    <!-- OPTIONAL (mandatory, when when source is compressed): Necessary for the in the packed OO document embedded files (mostly graphics from the compressed /Picture dir).
+         When the OpenOffice (OO) file has been unpacked the absoluteSoureDirRef can be taken,
+         Otherwise, a JAR URL could be choosen or when working with OpenOffice a so called Package-URL encoded over HTTP can be used to
+         access the jared contents of the the jared document. . -->
+    <xsl:param name="jaredRootURL" select="$absoluteSourceDirRef"/>
+
+    <!-- OPTIONAL (mandatory, when used in session based environment)
+         Useful for WebApplications: if a HTTP session is not cookie based, URL rewriting is beeing used (the session is appended to the URL).
+         This URL session is used when creating links to graphics by XSLT. Otherwise the user havt to log again in for every graphic he would like to see. -->
+    <xsl:param name="optionalURLSuffix"/>
+
+    <!-- OPTIONAL: DPI (dots per inch) the standard solution of given pictures (necessary for the conversion of 'cm' into 'pixel')-->
+    <!-- Although many pictures have the 96 dpi resolution, a higher resoltion give better results for common browsers -->
+    <xsl:param name="dpi" select="96"/>
+
+    <!-- OPTIONAL: in case of using a different processor than a JAVA XSLT, you can unable the Java functionality
+         (i.e. debugging time and encoding chapter names for the content-table as href and anchors ) -->
+    <xsl:param name="disableJava"    select="false"/>
+    <xsl:param name="isJavaDisabled" select="boolean($disableJava)"/>
+
+    <!-- OPTIONAL: user-agent will be differntiated by this parameter given by application (e.g. java servlet)-->
+    <xsl:param name="outputType" select="'CSS_HEADER'"/>
+    <!-- set of possible deviceTyps (WML is set in its own startfile main_wml.xsl):
+    <xsl:param name="outputType" select="'CSS_HEADER'"/>
+    <xsl:param name="outputType" select="'CSS_INLINED'"/>
+    <xsl:param name="outputType" select="'PALM'"/> -->
+
+    <!-- OPTIONAL: for activating the debug mode set the variable here to 'true()' or give any value from outside -->
+    <xsl:param name="debug"         select="false"/>
+    <xsl:param name="isDebugMode"   select="boolean($debug)"/>
+
+<!-- *************************************************************************
+    OPTIONAL: NEEDED IN CONNECTION WITH A GLOBAL DOCUMENT -->
+
+    <!--SUMMARY:
+         following parameter triggers a (quite time consuming) enabling of bookmarks in the table-of-content.
+        IN DETAIL:
+         Currently some links used in the Office XML (e.g. in the content table as '#7.Some%20Example%20Headline%7Outline')
+         is not a valid URL (cmp. bug id# 102311). No file destination is specified nor exist any anchor element for these
+         links in the Office XML.
+         A workaround for this transformation therefore had to be made. This time-consuming mechanism is disabled by default and
+         can be activated by a parameter (i.e. 'disableLinkedTableOfContent'). A creation of an anchor is made for each header element.
+         All header titles gonna be encoding to be usable in a relative URL.    -->
+    <xsl:param name="disableLinkedTableOfContent" select="false()"/>
+
+    <!-- The chapter numbers of the current document (as a sequence of a global document) is dependent of the number
+        of chapter of the same level in preceding documents. -->
+    <xsl:param name="precedingChapterLevel1"  select="0"/>
+    <xsl:param name="precedingChapterLevel2"  select="0"/>
+    <xsl:param name="precedingChapterLevel3"  select="0"/>
+    <xsl:param name="precedingChapterLevel4"  select="0"/>
+    <xsl:param name="precedingChapterLevel5"  select="0"/>
+    <xsl:param name="precedingChapterLevel6"  select="0"/>
+    <xsl:param name="precedingChapterLevel7"  select="0"/>
+    <xsl:param name="precedingChapterLevel8"  select="0"/>
+    <xsl:param name="precedingChapterLevel9"  select="0"/>
+    <xsl:param name="precedingChapterLevel10" select="0"/>
+
+    <!-- XML documents containing a table of contents,
+        gonna link for usability reason above each chapter to the preceding and following document and the content table -->
+    <xsl:param name="contentTableURL"/>
+
+    <!-- Needed for the bug workaround of missing content table links
+        by this ambigous HTML references from the content table can be evoided-->
+    <xsl:param name="globalDocumentRefToCurrentFile"/>
+
+    <!-- Needed for the bug workaround of missing content table links
+        by this node-set the relation between content-table link and children document header can be unambigous established -->
+    <xsl:param name="contentTableHeadings"/>
+
+
+<!-- END OF GLOBAL DOCUMENT SECTION
+*************************************************************************-->
+
+
+
+    <!-- works for normal separated zipped xml files as for flat filter single xml file format as well -->
+    <xsl:variable name="office:meta-file"           select="document($metaFileURL)"/>
+    <xsl:variable name="office:styles-file"         select="document($stylesFileURL)"/>
+    <xsl:variable name="office:font-decls"          select="$office:styles-file/*/office:font-decls"/>
+    <xsl:variable name="office:styles"              select="$office:styles-file/*/office:styles"/>
+    <!-- office:automatic-styles may occure in two different files (i.d. content.xml and styles.xml). Furthermore the top level tag is different in a flat xml file -->
+    <xsl:variable name="office:automatic-styles"    select="/*/office:automatic-styles"/>
+
+    <!-- simple declaration of WML used to avoid parser errors -->
+    <xsl:variable name="wap-paragraph-elements-without-table-row"/>
+    <xsl:variable name="wap-paragraph-elements"/>
+    <xsl:template name="wml-repeat-write-row"/>
+
+
+    <!-- ************************************* -->
+    <!-- *** build the propriate HTML file *** -->
+    <!-- ************************************* -->
+
+    <xsl:template match="/">
+
+        <!--<xsl:message>
+
+
+        Entered the styleSheets, transformation begins... </xsl:message>-->
+
+        <xsl:choose>
+            <xsl:when test="$isDebugMode">
+                <xsl:call-template name="check-parameter"/>
+
+                <xsl:if test="not($isJavaDisabled)">
+                    <xsl:call-template name="debug-style-collecting-time"/>
+                </xsl:if>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- to access the variable like a node-set it is necessary to convert it
+                     from a result-tree-fragment (RTF) to a node set using the James Clark extension -->
+                <xsl:variable name="collectedGlobalData-RTF">
+                    <xsl:call-template name='create-all-inline-styles'/>
+                </xsl:variable>
+
+                <xsl:choose>
+                    <xsl:when test="function-available('xt:node-set')">
+                        <xsl:call-template name="start">
+                            <xsl:with-param name="collectedGlobalData" select="xt:node-set($collectedGlobalData-RTF)"/>
+                        </xsl:call-template>
+                    </xsl:when>
+                    <xsl:when test="function-available('xalan:nodeset')">
+                        <xsl:call-template name="start">
+                            <xsl:with-param name="collectedGlobalData" select="xalan:nodeset($collectedGlobalData-RTF)"/>
+                        </xsl:call-template>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:element name="NodeSetFunctionNotAvailable"/>
+                        <xsl:call-template name="start"/>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template name="start">
+        <xsl:param name="collectedGlobalData"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+                <xsl:element name="html">
+                    <xsl:element name="head">
+                        <xsl:if test="$isDebugMode"><xsl:message>CSS helper variable will be created....</xsl:message></xsl:if>
+                        <xsl:call-template name='common-header-properties'/>
+                        <xsl:if test="$isDebugMode"><xsl:message>CSS variable ready, header will be created....</xsl:message></xsl:if>
+                        <!-- constructing the css header simulating inheritance of style-families by style order -->
+                        <xsl:call-template name='create-css-styleheader'/>
+                        <xsl:if test="$isDebugMode"><xsl:message>CSS header creation finished!</xsl:message></xsl:if>
+                    </xsl:element>
+
+
+
+                    <xsl:variable name="backgroundImageURL" select="$office:automatic-styles/style:page-master/style:properties/style:background-image/@xlink:href"/>
+                    <xsl:element name="body">
+                        <!-- background image -->
+                        <xsl:if test="$backgroundImageURL">
+                            <xsl:attribute name="background">
+                                <xsl:choose>
+                                    <!-- for images jared in open office document -->
+                                    <xsl:when test="contains($backgroundImageURL, '#Pictures/')">
+                                        <!-- creating an absolute http URL to the contained/packed image file -->
+                                        <xsl:value-of select="concat($jaredRootURL, '/Pictures/', substring-after($backgroundImageURL, '#Pictures/'), $optionalURLSuffix)"/>
+                                    </xsl:when>
+                                    <xsl:otherwise>
+                                        <xsl:attribute name="src"><xsl:value-of select="$backgroundImageURL"/></xsl:attribute>
+                                    </xsl:otherwise>
+                                </xsl:choose>
+                            </xsl:attribute>
+                        </xsl:if>
+
+                        <!-- processing the content of the xml file -->
+                        <xsl:apply-templates select="/*/office:body">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:apply-templates>
+                    </xsl:element>
+
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ HTML 4.0 INLINING  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:element name="html">
+                    <xsl:element name="head">
+                        <xsl:call-template name='common-header-properties'/>
+                    </xsl:element>
+
+                    <xsl:variable name="backgroundImageURL" select="$office:automatic-styles/style:page-master/style:properties/style:background-image/@xlink:href"/>
+                    <xsl:element name="body">
+                        <!-- background image -->
+                        <xsl:if test="$backgroundImageURL">
+                            <xsl:attribute name="background">
+                                <xsl:choose>
+                                    <!-- for images jared in open office document -->
+                                    <xsl:when test="contains($backgroundImageURL, '#Pictures/')">
+                                        <!-- creating an absolute http URL to the contained/packed image file -->
+                                        <xsl:value-of select="concat($jaredRootURL, '/Pictures/', substring-after($backgroundImageURL, '#Pictures/'), $optionalURLSuffix)"/>
+                                    </xsl:when>
+                                    <xsl:otherwise>
+                                        <xsl:attribute name="src"><xsl:value-of select="$backgroundImageURL"/></xsl:attribute>
+                                    </xsl:otherwise>
+                                </xsl:choose>
+                            </xsl:attribute>
+                        </xsl:if>
+                        <xsl:apply-templates select="/*/office:body">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:apply-templates>
+                    </xsl:element>
+                </xsl:element>
+            </xsl:when>
+
+            <!--+++++ PALM-VII (3.2 HTML SUBSET)  +++++-->
+            <xsl:when test="$outputType = 'PALM'">
+                <!-- the proxy will convert the html file later to PQA -->
+                <xsl:element name="html">
+                    <xsl:element name="head">
+                        <xsl:call-template name='palm-header-properties'/>
+                    </xsl:element>
+
+                    <xsl:element name="body">
+                        <!-- processing the content of the xml file -->
+                        <xsl:apply-templates select="/*/office:body">
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:apply-templates>
+                    </xsl:element>
+                </xsl:element>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <!-- ********************************************* -->
+    <!-- *** Header for CSS_INLINED and CSS_HEADER *** -->
+    <!-- ********************************************* -->
+
+    <xsl:template name='common-header-properties'>
+        <xsl:apply-templates select="$office:meta-file/*/office:meta/dc:title"/>
+        <xsl:apply-templates select="$office:meta-file/*/office:meta/dc:description"/>
+<!--2DO add further header elements..
+        <xsl:apply-templates select="$office:meta-file/*/office:meta/dc:subject"/>
+        <xsl:apply-templates select="$office:meta-file/*/office:meta/meta:keywords[postition()=1]"/>-->
+    </xsl:template>
+
+    <xsl:template match="dc:title">
+        <xsl:element name="title">
+            <xsl:value-of select="."/>
+        </xsl:element>
+    </xsl:template>
+
+    <xsl:template match="dc:description">
+        <xsl:element name="meta">
+            <xsl:attribute name="name">
+                <xsl:text>description</xsl:text>
+            </xsl:attribute>
+            <xsl:attribute name="content">
+                <xsl:value-of select="."/>
+            </xsl:attribute>
+        </xsl:element>
+    </xsl:template>
+
+
+    <!-- ********************************************* -->
+    <!-- *** Measuring the time for style creating *** -->
+    <!-- ********************************************* -->
+
+
+    <xsl:template name="debug-style-collecting-time">
+
+        <xsl:variable name="startTime-RTF">
+            <xsl:choose>
+                <xsl:when test="function-available('system:current-time-millis')">
+                    <xsl:value-of select="system:current-time-millis()"/>
+                </xsl:when>
+                <xsl:when test="function-available('java:java.lang.System.currentTimeMillis')">
+                    <xsl:value-of select="java:java.lang.System.currentTimeMillis()"/>
+                </xsl:when>
+            </xsl:choose>
+        </xsl:variable>
+
+
+
+        <xsl:variable name="collectedGlobalData-RTF">
+            <xsl:call-template name='create-all-inline-styles'/>
+        </xsl:variable>
+
+
+        <xsl:choose>
+            <xsl:when test="function-available('xt:node-set')">
+                <xsl:message>Creating the inline styles....</xsl:message>
+                <xsl:variable name="startTime"              select="number(xt:node-set($startTime-RTF))"/>
+                <xsl:variable name="collectedGlobalData"    select="xt:node-set($collectedGlobalData-RTF)"/>
+                <xsl:variable name="endTime"                select="system:current-time-millis()"/>
+
+                <xsl:message>Time for instantiating style variable: <xsl:value-of select="($endTime - $startTime)"/> ms</xsl:message>
+                <xsl:call-template name="start">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('xalan:nodeset')">
+                <xsl:message>Creating the inline styles....</xsl:message>
+                <xsl:variable name="startTime"              select="number(xalan:nodeset($startTime-RTF))"/>
+                <xsl:variable name="endTime"                select="java:java.lang.System.currentTimeMillis()"/>
+                <xsl:variable name="collectedGlobalData"    select="xalan:nodeset($collectedGlobalData-RTF)"/>
+
+                <xsl:message>Time for instantiating style variable: <xsl:value-of select="($endTime - $startTime)"/> ms</xsl:message>
+                <xsl:call-template name="start">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+
+    </xsl:template>
+
+    <!-- DEBUG purpose only: checking the parameters of this template-->
+    <xsl:template name="check-parameter">
+        <xsl:message>Parameter dpi: <xsl:value-of select="$dpi"/></xsl:message>
+        <xsl:message>Parameter metaFileURL: <xsl:value-of select="$metaFileURL"/></xsl:message>
+        <xsl:message>Parameter stylesFileURL: <xsl:value-of select="$stylesFileURL"/></xsl:message>
+        <xsl:message>Parameter absoluteSourceDirRef: <xsl:value-of select="$absoluteSourceDirRef"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel1 : <xsl:value-of select="$precedingChapterLevel1"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel2 : <xsl:value-of select="$precedingChapterLevel2"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel3 : <xsl:value-of select="$precedingChapterLevel3"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel4 : <xsl:value-of select="$precedingChapterLevel4"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel5 : <xsl:value-of select="$precedingChapterLevel5"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel6 : <xsl:value-of select="$precedingChapterLevel6"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel7 : <xsl:value-of select="$precedingChapterLevel7"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel8 : <xsl:value-of select="$precedingChapterLevel8"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel9 : <xsl:value-of select="$precedingChapterLevel9"/></xsl:message>
+        <xsl:message>Parameter precedingChapterLevel10: <xsl:value-of select="$precedingChapterLevel10"/></xsl:message>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/palm.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/palm.xsl
new file mode 100644 (file)
index 0000000..212edb1
--- /dev/null
@@ -0,0 +1,404 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+    <xsl:output cdata-section-elements="meta"/>
+
+
+    <!-- **************************** -->
+    <!-- *** specific palm header *** -->
+    <!-- **************************** -->
+
+    <xsl:template name='palm-header-properties'>
+        <xsl:element name="meta">
+            <xsl:attribute name="name">PalmComputingPlatform</xsl:attribute>
+            <xsl:attribute name="content">true</xsl:attribute>
+        </xsl:element>
+        <xsl:element name="meta">
+            <xsl:attribute name="name">HandheldFriendly</xsl:attribute>
+            <xsl:attribute name="content">true</xsl:attribute>
+        </xsl:element>
+        <xsl:element name="meta">
+            <xsl:attribute name="name">HistoryListText</xsl:attribute>
+            <xsl:attribute name="content">Dateimanager&#10;: &amp;date &amp;time</xsl:attribute>
+        </xsl:element>
+        <xsl:element name="meta">
+            <xsl:attribute name="name">description</xsl:attribute>
+            <xsl:attribute name="content">StarPortal</xsl:attribute>
+        </xsl:element>
+        <xsl:element name="meta">
+            <xsl:attribute name="name">keywords</xsl:attribute>
+            <xsl:attribute name="content">starportal, staroffice, software</xsl:attribute>
+        </xsl:element>
+        <xsl:element name="meta">
+            <xsl:attribute name="http-equiv">Content-Type</xsl:attribute>
+            <xsl:attribute name="content">text/html; charset=iso-8859-1</xsl:attribute>
+        </xsl:element>
+    </xsl:template>
+
+
+    <!-- ********************************* -->
+    <!-- *** creating table attributes *** -->
+    <!-- ********************************* -->
+
+    <!-- table data (td) and table header (th) attributes -->
+    <xsl:template name="create-attribute-ALIGN">
+        <xsl:param name="styleProperties"/>
+
+        <xsl:if test="contains($styleProperties, 'align')">
+            <xsl:attribute name="align">
+                 <xsl:choose>
+                    <xsl:when test="contains($styleProperties, 'align:left')">
+                        <xsl:text>left</xsl:text>
+                    </xsl:when>
+                    <xsl:when test="contains($styleProperties, 'align:right')">
+                        <xsl:text>right</xsl:text>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:text>center</xsl:text>
+                    </xsl:otherwise>
+                 </xsl:choose>
+            </xsl:attribute>
+        </xsl:if>
+    </xsl:template>
+
+
+    <!-- ********************************* -->
+    <!-- *** creating List attributes  *** -->
+    <!-- ********************************* -->
+<!--
+    <xsl:template name="create-list-attributes">
+        <xsl:param name="styleProperties"/>
+
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+2 be implemented
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+    </xsl:template>
+-->
+
+    <!-- ************************************************ -->
+    <!-- *** creating nested format tags (PALM & WML) *** -->
+    <!-- ************************************************ -->
+
+    <!-- Italic -->
+    <xsl:template name="create-nested-format-tags">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+        <xsl:choose>
+            <xsl:when test="contains($styleProperties, 'italic')">
+                <xsl:element name="i">
+                    <xsl:call-template name="bold">
+                        <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:call-template name="bold">
+                    <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- Bold -->
+    <xsl:template name="bold">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($styleProperties, 'bold')">
+                <xsl:element name="b">
+                    <xsl:call-template name="underline">
+                        <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:call-template name="underline">
+                    <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- Underline : last format attribute, which is also used from WML - WML ends here! -->
+    <xsl:template name="underline">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+
+        <xsl:choose>
+            <xsl:when test="$outputType = 'PALM'">
+                <xsl:choose>
+                    <xsl:when test="contains($styleProperties, 'underline')">
+                        <xsl:element name="u">
+                            <xsl:call-template name="strikethrough">
+                                <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:call-template>
+                        </xsl:element>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:call-template name="strikethrough">
+                            <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:call-template>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:choose>
+                    <xsl:when test="contains($styleProperties, 'underline')">
+                        <xsl:element name="u">
+                            <xsl:apply-templates>
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:apply-templates>
+                        </xsl:element>
+                    </xsl:when>
+                    <xsl:otherwise>
+                        <xsl:apply-templates>
+                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        </xsl:apply-templates>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- strikethrough -->
+    <xsl:template name="strikethrough">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($styleProperties, 'strike')">
+                <xsl:element name="strike">
+                    <xsl:call-template name="align">
+                        <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:call-template name="align">
+                    <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- Alignment -->
+    <xsl:template name="align">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($styleProperties, 'align')">
+                <xsl:element name="div">
+                    <xsl:attribute name="align">
+                         <xsl:choose>
+                            <xsl:when test="contains($styleProperties, 'align:left')">
+                                <xsl:text>left</xsl:text>
+                            </xsl:when>
+                            <xsl:when test="contains($styleProperties, 'align:right')">
+                                <xsl:text>right</xsl:text>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:text>center</xsl:text>
+                            </xsl:otherwise>
+                         </xsl:choose>
+                    </xsl:attribute>
+                    <xsl:call-template name="font_combined">
+                        <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:call-template>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:call-template name="font_combined">
+                    <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- Both size and Color for font -->
+    <xsl:template name="font_combined">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($styleProperties, 'color') and contains($styleProperties, 'size')">
+                <xsl:element name="font">
+
+                    <xsl:attribute name="color">
+                         <xsl:choose>
+                            <xsl:when test="contains($styleProperties, 'color:#000000')">
+                                <xsl:text>#000000</xsl:text>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:text>#FFFFFF</xsl:text>
+                            </xsl:otherwise>
+                         </xsl:choose>
+                    </xsl:attribute>
+
+                    <xsl:attribute name="size">
+                        <xsl:value-of select="substring-after(substring-before($styleProperties ,':size'), 'size:')"/>
+                    </xsl:attribute>
+
+                    <!-- get the embedded content -->
+                    <xsl:apply-templates>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:apply-templates>
+                </xsl:element>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:call-template name="font_simple">
+                    <xsl:with-param name="styleProperties" select="$styleProperties"/>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- size or Color for font -->
+    <xsl:template name="font_simple">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="styleProperties"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($styleProperties, 'color')">
+                <xsl:element name="font">
+                    <xsl:attribute name="color">
+                         <xsl:choose>
+                            <xsl:when test="contains($styleProperties, 'color:#000000')">
+                                <xsl:text>#000000</xsl:text>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:text>#FFFFFF</xsl:text>
+                            </xsl:otherwise>
+                         </xsl:choose>
+                    </xsl:attribute>
+
+                    <!-- get the embedded content -->
+                    <xsl:apply-templates>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:apply-templates>
+                </xsl:element>
+            </xsl:when>
+
+            <xsl:when test="contains($styleProperties, 'size')">
+                <xsl:element name="font">
+                    <xsl:attribute name="size">
+                        <xsl:value-of select="substring-after(substring-before($styleProperties ,':size'), 'size:')"/>
+                    </xsl:attribute>
+
+                    <!-- get the embedded content -->
+                    <xsl:apply-templates>
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    </xsl:apply-templates>
+                </xsl:element>
+            </xsl:when>
+
+            <xsl:otherwise>
+                <!-- get the embedded content -->
+                <xsl:apply-templates>
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                </xsl:apply-templates>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/style_header.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/style_header.xsl
new file mode 100644 (file)
index 0000000..eeb0c20
--- /dev/null
@@ -0,0 +1,379 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+
+    <!-- ****************************** -->
+    <!-- *** style sheet processing *** -->
+    <!-- ****************************** -->
+
+
+    <xsl:template name='create-css-styleheader'>
+        <xsl:comment>
+            <xsl:text>The CSS style header method for setting styles</xsl:text>
+        </xsl:comment>
+        <xsl:element name="style">
+            <xsl:attribute name="type">text/css</xsl:attribute>
+            <xsl:comment>
+                <xsl:text>
+
+        </xsl:text>
+                <xsl:call-template name="write-default-styles"/>
+
+                <!-- THE STYLE PROPERTIES OF THE FIRST WRITTEN STYLE (PARENT) IS GIVEN OUT -->
+
+                <!-- 1) styles from office:styles are possible parent from all (itself or office:automatic-styles).
+                     Therefore they are created first.
+                     Beginning with the top-level parents (the styles without any parent). -->
+                <xsl:for-each select="$office:styles/style:style[not(@style:parent-style-name)]">
+
+                    <xsl:call-template name="write-styleproperty-line"/>
+                    <xsl:call-template name="write-styleproperty-lines-for-children"/>
+                </xsl:for-each>
+
+                <xsl:text> </xsl:text>
+
+                <!-- 2) styles from office:automatic-styles can only be parent of styles from the office:automatic-styles section.
+                     Beginning with top-level styles, again, all children style will be recursivly traversed -->
+                <xsl:for-each select="$office:automatic-styles/style:style[not(@style:parent-style-name)]">
+                    <xsl:call-template name="write-styleproperty-line">
+                        <xsl:with-param name="searchOnlyInAutomaticStyles" select="true()"/>
+                    </xsl:call-template>
+                    <xsl:call-template name="write-styleproperty-lines-for-children">
+                        <xsl:with-param name="searchOnlyInAutomaticStyles"/>
+                    </xsl:call-template>
+                </xsl:for-each>
+            //</xsl:comment>
+        </xsl:element>
+    </xsl:template>
+
+
+    <xsl:template name='write-styleproperty-line'>
+        <xsl:param name="searchOnlyInAutomaticStyles"/>
+
+        <xsl:variable name="styleProperties">
+            <xsl:call-template name="write-style-properties">
+                <xsl:with-param name="styleAttributePath"   select="current()/style:properties/@*"/>
+            </xsl:call-template>
+        </xsl:variable>
+
+        <!-- do not write styles with no css property -->
+        <xsl:if test="not(string-length($styleProperties) = 0)">
+            <!-- write out the name of the current (parent) style in the CSS headersection (e.g. "span.myStyle") -->
+            <xsl:call-template name="write-style-name">
+                <xsl:with-param name="is-parent-style" select="true()"/>
+            </xsl:call-template>
+
+            <!-- the names of all styles children will be written out(office:style AND office:automatic-style) -->
+            <xsl:call-template name="write-children-style-names">
+                <xsl:with-param name="parentStyleName"          select="@style:name"/>
+                <xsl:with-param name="parentStyleFamily"        select="@style:family"/>
+                <xsl:with-param name="searchOnlyInAutomaticStyles"/>
+            </xsl:call-template>
+
+        <!-- the style properties of the first written style (parent) is given out -->
+        <xsl:text> {
+                </xsl:text>
+                <xsl:value-of select="$styleProperties"/>
+        <xsl:text>}
+        </xsl:text>
+
+        </xsl:if>
+
+
+
+    </xsl:template>
+
+
+
+
+    <!-- RECURSION WITH ENDCONDITON: adding style classes for all existing childs -->
+    <xsl:template name='write-styleproperty-lines-for-children'>
+        <xsl:param name="searchOnlyInAutomaticStyles"/>
+
+        <xsl:variable name="parentStyleName"    select="@style:name"/>
+        <xsl:variable name="parentStyleFamily"  select="@style:family"/>
+
+        <xsl:if test="not(searchOnlyInAutomaticStyles)">
+            <xsl:for-each select="../style:style[@style:family=$parentStyleFamily and @style:parent-style-name=$parentStyleName]">
+                <xsl:call-template name="write-styleproperty-line"/>
+                <xsl:call-template name="write-styleproperty-lines-for-children"/>
+            </xsl:for-each>
+        </xsl:if>
+        <xsl:for-each select="$office:automatic-styles/style:style[@style:family=$parentStyleFamily and @style:parent-style-name=$parentStyleName]">
+            <xsl:call-template name="write-styleproperty-line">
+                <xsl:with-param name="searchOnlyInAutomaticStyles"/>
+            </xsl:call-template>
+            <xsl:call-template name="write-styleproperty-lines-for-children">
+                <xsl:with-param name="searchOnlyInAutomaticStyles"/>
+            </xsl:call-template>
+        </xsl:for-each>
+    </xsl:template>
+
+
+    <xsl:template name="write-default-styles">
+
+        <!-- some default attributes in xml have to be explicitly set in HTML (e.g. margin-top="0") -->
+        <xsl:text>*.OOo_defaults</xsl:text>
+
+                <xsl:for-each select="$office:styles/style:style">
+                    <xsl:text>, </xsl:text>
+                    <xsl:value-of select="concat('*.', translate(@style:name, '. %()/\', ''))"/>
+                </xsl:for-each>
+
+                <xsl:for-each select="$office:automatic-styles/style:style">
+                    <xsl:text>, </xsl:text>
+                    <xsl:value-of select="concat('*.', translate(@style:name, '. %()/\', ''))"/>
+                </xsl:for-each>
+        <!-- 2DO: the defaults might be better collected and written in a separated (XML) file -->
+<xsl:text> {
+                margin-top:0cm; margin-bottom:0cm; }
+        </xsl:text>
+
+        <xsl:for-each select="$office:styles/style:default-style">
+            <xsl:call-template name="write-default-style"/>
+        </xsl:for-each>
+
+        <xsl:for-each select="$office:automatic-styles/style:default-style">
+            <xsl:call-template name="write-default-style"/>
+        </xsl:for-each>
+
+    </xsl:template>
+
+
+
+    <xsl:template name="write-default-style">
+        <xsl:variable name="family-style" select="@style:family"/>
+
+        <!-- some default attributes for format families (e.g. graphics, paragraphs, etc.) written as style:default-style -->
+        <xsl:value-of select="concat('*.', translate($family-style, '. %()/\', ''), '_defaults')"/>
+
+        <xsl:for-each select="$office:styles/style:style[@style:family = $family-style]">
+            <xsl:text>, </xsl:text>
+            <xsl:value-of select="concat('*.', translate(@style:name, '. %()/\', ''))"/>
+        </xsl:for-each>
+
+        <xsl:for-each select="$office:automatic-styles/style:style[@style:family = $family-style]">
+            <xsl:text>, </xsl:text>
+            <xsl:value-of select="concat('*.', translate(@style:name, '. %()/\', ''))"/>
+        </xsl:for-each>
+
+
+        <xsl:variable name="styleProperties">
+            <xsl:call-template name="write-style-properties">
+                <xsl:with-param name="styleAttributePath"   select="current()/style:properties/@*"/>
+            </xsl:call-template>
+        </xsl:variable>
+
+        <!-- do not write styles with no css property -->
+        <xsl:if test="not(string-length($styleProperties) = 0)">
+        <!-- the style properties of the first written style (parent) is given out -->
+        <xsl:text> {
+                </xsl:text>
+                <xsl:value-of select="$styleProperties"/>
+        <xsl:text>}
+        </xsl:text>
+        </xsl:if>
+
+    </xsl:template>
+
+
+    <!--++
+          The parent style will be written out!
+          For each Style:family a prefix must be added
+            <!ENTITY % styleFamily
+            "(paragraph|text|section|table|table-column|table-row|table-cell|table-page|chart|graphics|default|drawing-page|presentation|control)">
+        ++-->
+    <xsl:template name="write-style-name">
+        <xsl:param name="is-parent-style"/>
+
+        <!-- This construct is for list elements. Whenever a paragraph element is being used as child of a list element the name paragraph style is been used for
+            the list item. This can be switched as the paragaph style-name and the list-style-name are in the same element.
+            Otherwise there would be formatting errors (e.g. margin-left will be used for the content in the list elment and not for the list element itself). -->
+        <xsl:variable name="style-name">
+            <xsl:choose>
+                <xsl:when test="@style:list-style-name">
+                    <xsl:value-of select="@style:list-style-name"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:value-of select="@style:name"/>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:variable>
+
+        <xsl:if test="not($is-parent-style)">
+            <xsl:text>, </xsl:text>
+        </xsl:if>
+
+        <xsl:choose>
+            <!-- normally 'p.' would be used as CSS element,
+                 but header (h1, h2,...) are also from the style:family paragraph -->
+            <xsl:when test="@style:family='paragraph'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='text'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='section'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='table'">
+                <xsl:value-of select="concat('table.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='table-column'">
+            <!-- as column styles have to be included as span styles AFTER the table (no two class attributes in TD allowed -->
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='table-row'">
+                <xsl:value-of select="concat('tr.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='table-cell'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='table-page'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='chart'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='graphics'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='default'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='drawing-page'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='presentation'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+            <xsl:when test="@style:family='control'">
+                <xsl:value-of select="concat('*.', translate($style-name, '. %()/\', ''))"/>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- finding all style child of a section and give their styleIdentifier to the output -->
+    <xsl:template name='write-children-style-names'>
+        <xsl:param name="parentStyleName" select="@style:name"/>
+        <xsl:param name="parentStyleFamily" select="@style:family"/>
+        <xsl:param name="searchOnlyInAutomaticStyles"/>
+
+
+        <!--** the names of all office:styles children will be written out
+            ** (a automatic style can only have children in the office:automatic-style section) -->
+
+        <!-- if NOT called from a office:automatic-style parent -->
+        <xsl:if test="not(searchOnlyInAutomaticStyles)">
+            <!-- for all children in the office:style section -->
+            <xsl:for-each select="../style:style[@style:family=$parentStyleFamily and @style:parent-style-name=$parentStyleName]">
+                <!-- write the style name in the css header -->
+                <xsl:call-template name="write-style-name"/>
+
+                <!-- search for child styles -->
+                <xsl:call-template name="write-children-style-names">
+                    <xsl:with-param name="parentStyleName" select="@style:name"/>
+                    <xsl:with-param name="parentStyleFamily" select="@style:family"/>
+                </xsl:call-template>
+
+            </xsl:for-each>
+        </xsl:if>
+
+        <!--** the names of all office:automatic-styles children will be written out -->
+
+        <!-- for all children in the office:automatic-style section -->
+        <xsl:for-each select="$office:automatic-styles/style:style[@style:family=$parentStyleFamily and @style:parent-style-name=$parentStyleName]">
+            <!-- write the style name in the css header -->
+            <xsl:call-template name="write-style-name"/>
+
+            <!-- search for child styles -->
+            <xsl:call-template name="write-children-style-names">
+                <xsl:with-param name="parentStyleName" select="@style:name"/>
+                <xsl:with-param name="parentStyleFamily" select="@style:family"/>
+                <xsl:with-param name="searchOnlyInAutomaticStyles"/>
+            </xsl:call-template>
+
+        </xsl:for-each>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/style_inlined.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/style_inlined.xsl
new file mode 100644 (file)
index 0000000..1915995
--- /dev/null
@@ -0,0 +1,398 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+
+    <!-- ********************************************* -->
+    <!-- *** hard attributed (inlined) properties  *** -->
+    <!-- ********************************************* -->
+
+
+    <!-- RESTRICTIONS:
+            1)  As the styles-node-variables are NOT global, the style variables are not global, either!!
+            2)  As a list of elements can only be added to a variable as a result tree fragment the
+                extension is neccessary!!
+    -->
+
+    <!-- 2DO: Inline styles do not inherit from XML office defaults nor font:family defaults as the style header does
+              (cp. stylesheet 'style_header.xsl' and the 'write-default-styles' template) -->
+
+    <xsl:template name='create-all-inline-styles'>
+
+        <!--** traversee all style trees and their branches collecting style properties **-->
+        <xsl:element name="allstyles">
+        <!--** traversee all office:styles trees beginning with the top-level styles**-->
+            <xsl:for-each select="$office:styles/style:style[not(@style:parent-style-name)]">
+
+                <!--** give out the style properties of the parent node **-->
+                <xsl:call-template name='write-current-and-inherited-style-properties'>
+                    <xsl:with-param name="styles-node"                  select="$office:styles"/>
+                    <xsl:with-param name="style-family"                 select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+
+                <!--** all office:styles children of the current top-level office:styles **-->
+                <xsl:call-template name='for-all-templates-child-styles'>
+                    <xsl:with-param name="parentStyleName"              select="@style:name"/>
+                    <xsl:with-param name="parentStyleFamily"            select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+
+                <!--** all office:automatic-styles children of the current top-level style **-->
+                <xsl:call-template name='for-all-automatic-child-styles'>
+                    <xsl:with-param name="parentStyleName"              select="@style:name"/>
+                    <xsl:with-param name="parentStyleFamily"            select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+            </xsl:for-each>
+
+        <!--** traversee all office:automatic-styles trees beginning with the top-level styles **-->
+            <xsl:for-each select="$office:automatic-styles/style:style[not(@style:parent-style-name)]">
+                <!--** give out the style properties of the parent node **-->
+                <xsl:call-template name='write-current-and-inherited-style-properties'>
+                    <xsl:with-param name="styles-node"                  select="$office:automatic-styles"/>
+                    <xsl:with-param name="style-family"                 select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+
+                <!--** all children of the top-level office:automatic-styless  **-->
+                <xsl:call-template name='for-all-automatic-child-styles'>
+                    <xsl:with-param name="parentStyleName"              select="@style:name"/>
+                    <xsl:with-param name="parentStyleFamily"            select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+            </xsl:for-each>
+        </xsl:element>
+    </xsl:template>
+
+
+
+    <xsl:template name='for-all-templates-child-styles'>
+        <xsl:param name="parentStyleName"/>
+        <xsl:param name="parentStyleFamily"/>
+        <xsl:param name="style-name-tokenized"/>
+
+        <xsl:for-each select="../style:style[@style:family=$parentStyleFamily and @style:parent-style-name=$parentStyleName]">
+            <!--** give out the style properties of the current node **-->
+            <xsl:element name="{$style-name-tokenized}">
+                <xsl:call-template name='write-current-and-inherited-style-properties'>
+                    <xsl:with-param name="styles-node"                  select="$office:styles"/>
+                    <xsl:with-param name="style-family"                 select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+            </xsl:element>
+
+            <!--** for all template-children of the current office:styles  **-->
+            <xsl:call-template name='for-all-templates-child-styles'>
+                <xsl:with-param name="styles-node"                  select="$office:styles"/>
+                <xsl:with-param name="parentStyleName"              select="@style:name"/>
+                <xsl:with-param name="parentStyleFamily"            select="@style:family"/>
+                <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+            </xsl:call-template>
+
+            <!--** for all automatic-children of the current office:styles  **-->
+            <xsl:call-template name='for-all-automatic-child-styles'>
+                <xsl:with-param name="styles-node"                  select="$office:automatic-styles"/>
+                <xsl:with-param name="parentStyleName"              select="@style:name"/>
+                <xsl:with-param name="parentStyleFamily"            select="@style:family"/>
+                <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+            </xsl:call-template>
+
+        </xsl:for-each>
+    </xsl:template>
+
+
+
+    <xsl:template name='for-all-automatic-child-styles'>
+        <xsl:param name="styles-node"/>
+        <xsl:param name="parentStyleName"/>
+        <xsl:param name="parentStyleFamily"/>
+        <xsl:param name="style-name-tokenized"/>
+
+        <xsl:for-each select="$office:automatic-styles/style:style[@style:family=$parentStyleFamily and @style:parent-style-name=$parentStyleName]">
+            <!--** give out the style properties of the current node **-->
+            <xsl:element name="{$style-name-tokenized}">
+                <xsl:call-template name='write-current-and-inherited-style-properties'>
+                    <xsl:with-param name="styles-node"                  select="$office:automatic-styles"/>
+                    <xsl:with-param name="style-family"                 select="@style:family"/>
+                    <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+                </xsl:call-template>
+            </xsl:element>
+
+            <!--** for all automatic-children of the current office:automatic-styles  **-->
+            <xsl:call-template name='for-all-automatic-child-styles'>
+                <xsl:with-param name="styles-node"                  select="$office:automatic-styles"/>
+                <xsl:with-param name="parentStyleName"              select="@style:name"/>
+                <xsl:with-param name="parentStyleFamily"            select="@style:family"/>
+                <xsl:with-param name="style-name-tokenized"         select="translate(@style:name, '. %()/\', '')"/>
+            </xsl:call-template>
+        </xsl:for-each>
+    </xsl:template>
+
+
+    <xsl:template name='write-current-and-inherited-style-properties'>
+        <xsl:param name="style-family"/>
+        <xsl:param name="styles-node"/>
+        <xsl:param name="style-name-tokenized"/>
+
+        <xsl:element name="{$style-name-tokenized}">
+            <xsl:variable name="current-style-name" select="@style:name"/>
+            <xsl:variable name="parent-style-name" select="@style:parent-style-name"/>
+
+            <xsl:variable name="new-property-list">
+                <!--*** COLLECT STYLE ATTRIBUTES (only toplevel) ***-->
+                <xsl:call-template name="write-style-properties">
+                    <xsl:with-param name="styleAttributePath"   select="$styles-node/style:style[@style:family=$style-family and @style:name=$current-style-name]/style:properties/@*"/>
+                </xsl:call-template>
+            </xsl:variable>
+            <xsl:choose>
+                <!--*** @End: GIVE OUT All COLLECTED STYLE ATTRIBUTES (only toplevel) ***-->
+                <xsl:when test="string-length($parent-style-name)=0">
+                <!--** if no styleParent is given, the properties are given out at once **-->
+                    <xsl:value-of select="normalize-space($new-property-list)"/>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:variable name="new-property-names">
+                        <xsl:for-each select="$styles-node/style:style[@style:family=$style-family and @style:name=$current-style-name]/style:properties/@*">
+                            <xsl:value-of select="name()"/>
+                        </xsl:for-each>
+                    </xsl:variable>
+                    <!--** further attributes of the parent style must be collected **-->
+                    <xsl:call-template name="add-parent-style-attributes">
+                        <xsl:with-param name="property-name-list"       select="$new-property-names"/>
+                        <xsl:with-param name="complete-property-list"   select="normalize-space($new-property-list)"/>
+                        <xsl:with-param name="current-style-name"       select="$current-style-name"/>
+                        <xsl:with-param name="parent-style-name"        select="$parent-style-name"/>
+                        <xsl:with-param name="style-family"             select="$style-family"/>
+                    </xsl:call-template>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:element>
+    </xsl:template>
+
+
+
+    <xsl:template name="add-parent-style-attributes">
+        <xsl:param name="property-name-list"/>
+        <xsl:param name="complete-property-list"/>
+        <xsl:param name="current-style-name"/>
+        <xsl:param name="parent-style-name"/>
+        <xsl:param name="style-family"/>
+
+        <!--*** New two be added property names will be collected (only one variable per template) ***-->
+        <xsl:variable name="new-property-names">
+            <xsl:call-template name="get-new-style-names">
+                <xsl:with-param name="property-name-list"       select="$property-name-list"/>
+                <xsl:with-param name="parent-style-name"        select="$parent-style-name"/>
+                <xsl:with-param name="current-style-name"       select="$current-style-name"/>
+            </xsl:call-template>
+        </xsl:variable>
+
+        <xsl:choose>
+            <!--*** check if the new parent style exist in the office:automatic-styles section (defined by name and family) ***-->
+            <xsl:when test="$office:automatic-styles/style:style[@style:family='paragraph' and @style:name=$current-style-name]">
+                <!--*** RECURSION: adding new parent style attributes to the current style ***-->
+                <xsl:variable name="new-property-attributes">
+                    <xsl:call-template name="get-new-style-attributes">
+                        <xsl:with-param name="new-property-names"           select="$new-property-names"/>
+                        <xsl:with-param name="current-style-name"           select="$current-style-name"/>
+                        <xsl:with-param name="parent-style-name"            select="$parent-style-name"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <!--*** End CONDITION: the last style parent has already been executed ***-->
+                <xsl:variable name="new-parent-style-name"  select="$office:automatic-styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]/@style:parent-style-name"/>
+                <xsl:choose>
+                    <xsl:when test="string-length($new-parent-style-name)=0">
+                    <!--** no further parent is found, the given parameter property-node is the final style -->
+                        <xsl:value-of select="concat($complete-property-list,$new-property-attributes)"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                    <!--** further attributes of the parent style must be collected **-->
+                        <xsl:call-template name="add-parent-style-attributes">
+                            <xsl:with-param name="property-name-list"       select="concat($property-name-list, $new-property-names)"/>
+                            <xsl:with-param name="complete-property-list"   select="concat($complete-property-list,$new-property-attributes)"/>
+                            <xsl:with-param name="current-style-name"       select="$parent-style-name"/>
+                            <xsl:with-param name="parent-style-name"        select="$new-parent-style-name"/>
+                            <xsl:with-param name="style-family"             select="$style-family"/>
+                        </xsl:call-template>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:when>
+
+            <!--** the specific style (defined by name and family) must be found in the office:styles section -->
+            <xsl:otherwise>
+                <!--*** RECURSION: adding new parent style attributes to the current style ***-->
+                <!--*** adding new parent style attributes to the current style ***-->
+                <xsl:variable name="new-property-attributes">
+                    <xsl:call-template name="get-new-style-attributes">
+                        <xsl:with-param name="new-property-names"           select="$new-property-names"/>
+                        <xsl:with-param name="current-style-name"           select="$current-style-name"/>
+                        <xsl:with-param name="parent-style-name"            select="$parent-style-name"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <!--*** End CONDITION: the last style parent has already been executed ***-->
+                <xsl:variable name="new-parent-style-name"  select="$office:styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]/@style:parent-style-name"/>
+                <xsl:choose>
+                    <xsl:when test="string-length($new-parent-style-name)=0">
+                    <!--** no further parent is found, the given parameter property-node is the final style -->
+                        <xsl:value-of select="concat($complete-property-list,$new-property-attributes)"/>
+                    </xsl:when>
+                    <xsl:otherwise>
+                    <!--** further attributes of the parent style must be collected **  -->
+                        <xsl:call-template name="add-parent-style-attributes">
+                            <xsl:with-param name="property-name-list"       select="concat($property-name-list, $new-property-names)"/>
+                            <xsl:with-param name="complete-property-list"   select="concat($complete-property-list,$new-property-attributes)"/>
+                            <xsl:with-param name="current-style-name"       select="$parent-style-name"/>
+                            <xsl:with-param name="parent-style-name"        select="$new-parent-style-name"/>
+                            <xsl:with-param name="style-family"             select="$style-family"/>
+                        </xsl:call-template>
+                    </xsl:otherwise>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <xsl:template name="get-new-style-names">
+        <xsl:param name="property-name-list"/>
+        <xsl:param name="parent-style-name"/>
+        <xsl:param name="current-style-name"/>
+        <!--** where to find the specific style (defined by name and family) wheter in office:automatic-styles or office:styles section -->
+        <xsl:choose>
+            <!--** if the specific style (defined by name and family) can be found in the office:automatic-styles section -->
+            <xsl:when test="$office:automatic-styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]">
+                <xsl:variable name="parent-property-node" select="$office:automatic-styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]/style:properties"/>
+
+                <xsl:variable name="new-property-name-list">
+                    <xsl:for-each select="$parent-property-node/@*[not(contains($property-name-list, name()))]">
+                        <xsl:value-of select="name()"/>
+                    </xsl:for-each>
+                </xsl:variable>
+                <xsl:value-of select="$new-property-name-list"/>
+            </xsl:when>
+            <!--** the specific style (defined by name and family) should be found in the office:styles section -->
+            <xsl:otherwise>
+                <xsl:variable name="parent-property-node" select="$office:styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]/style:properties"/>
+                <xsl:variable name="new-property-name-list">
+                    <xsl:for-each select="$parent-property-node/@*[not(contains($property-name-list, name()))]">
+                        <xsl:value-of select="name()"/>
+                    </xsl:for-each>
+                </xsl:variable>
+                <xsl:value-of select="$new-property-name-list"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <xsl:template name="get-new-style-attributes">
+        <xsl:param name="new-property-names"/>
+        <xsl:param name="current-style-name"/>
+        <xsl:param name="parent-style-name"/>
+
+        <!--** where to find the specific style (defined by name and family) whether in office:automatic-styles or office:styles section -->
+        <xsl:choose>
+            <!--** if the specific style (defined by name and family) can be found in the office:automatic-styles section -->
+            <xsl:when test="$office:automatic-styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]">
+                <xsl:variable name="parent-property-node" select="$office:automatic-styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]/style:properties"/>
+                <xsl:variable name="new-property-name-list">
+                    <xsl:call-template name="write-style-properties">
+                        <xsl:with-param name="styleAttributePath"   select="$parent-property-node/@*[contains($new-property-names, name())]"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:value-of select="normalize-space($new-property-name-list)"/>
+            </xsl:when>
+            <!--** otherwise the specific style (defined by name and family) should be found in the office:styles section -->
+            <xsl:otherwise>
+                <xsl:variable name="parent-property-node" select="$office:styles/style:style[@style:family='paragraph' and @style:name=$parent-style-name]/style:properties"/>
+                <xsl:variable name="new-property-name-list">
+                    <xsl:call-template name="write-style-properties">
+                        <xsl:with-param name="styleAttributePath"   select="$parent-property-node/@*[contains($new-property-names, name())]"/>
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:value-of select="normalize-space($new-property-name-list)"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/style_mapping.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/style_mapping.xsl
new file mode 100644 (file)
index 0000000..a9a858d
--- /dev/null
@@ -0,0 +1,660 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+    <xsl:template name="write-style-properties">
+        <xsl:param name="styleAttributePath"/>
+
+        <xsl:choose>
+            <!--+++++ CSS PROPERTIES  +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER' or $outputType = 'CSS_INLINED'">
+
+                <xsl:for-each select="$styleAttributePath">
+                <!-- isDebugModeMESSAGE:
+                    <xsl:message> Name:<xsl:value-of select="name()"/> Value:<xsl:value-of select="."/></xsl:message>      -->
+
+
+                    <!-- <!ATTLIST style:properties style:horizontal-pos (from-left|left|center|right|from-inside|inside|outside)#IMPLIED>-->
+                    <!-- 2DO: is inside/from-inside also better showable ? -->
+                    <!-- !!!! 2DO: Still there have to be placed a <br clear='all'/> to disable the flow!!!!-->
+                    <!--           The OOo attribute 'style:number-wrapped-paragraphs' is currently ignored -->
+                    <xsl:choose>
+                        <xsl:when test='name(.)="style:wrap"'>
+                            <xsl:choose>
+                                <xsl:when test='.="left"'>
+                                    <xsl:text>float: right; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test='.="right"'>
+                                    <xsl:text>float: left; </xsl:text>
+                                </xsl:when>
+                            </xsl:choose>
+                        </xsl:when>
+
+                        <xsl:when test='name(.) = "style:horizontal-pos"'>
+                            <xsl:choose>
+                                <xsl:when test='.="left"'>
+                                    <xsl:text>align: left; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test='.="right"'>
+                                    <xsl:text>align: right; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test='.="center"'>
+                                    <xsl:text>align: center; </xsl:text>
+                                </xsl:when>
+                            </xsl:choose>
+                        </xsl:when>
+<!-- results into a bad view (overlapped) in Mozilla 1.0
+                        <xsl:when test='name(.) = "table:align"'>
+                            <xsl:choose>
+                                <xsl:when test='.="left"'>
+                                    <xsl:text>float: right; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test='.="right"'>
+                                    <xsl:text>float: left; </xsl:text>
+                                </xsl:when>
+                            </xsl:choose>
+                        </xsl:when>
+-->
+
+                        <!-- PADDING for all variations: fo:padding, fo:padding-top, fo:padding-bottom, fo:padding-left, fo:padding-right -->
+                        <xsl:when test='contains(name(.),"fo:padding")'>
+                            <xsl:text>padding: </xsl:text>
+                            <xsl:value-of select="."/>
+                            <xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <!--
+                        fo:border
+                        fo:border-top
+                        fo:border-bottom
+                        fo:border-left
+                        fo:border-right
+
+                            At present, all four borders must be set simultaneously by using either
+                            the fo:border property or by attaching all four of the other border
+                            properties to an item set element. In the latter case, if one or more
+                            of the properties is missing their values are assumed to be none. The
+                            only border styles supported are none or hidden, solid, and double. Any
+                            other border style specified is displayed as solid. Transparent borders
+                            are not supported and the border widths thin, medium, and thick are
+                            mapped to lengths. In addition, only some distinct border widths are
+                            supported. Unsupported widths are rounded up to the next supported
+                            width.
+                            If there are no padding properties specified within the same
+                            item set element, a default padding is used for sides that have a
+                            border. A value of 0cm is used for sides without a border.
+                            (cp. wd-so-xml-text.sdw)
+                        -->
+
+<!--2DO START: change measurement equally -->
+                        <xsl:when test='name(.)="fo:border"'>
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>border-width:</xsl:text><xsl:value-of select="substring-before(.,'ch ')"/><xsl:text>; </xsl:text>
+                                    <xsl:text>border-style:</xsl:text><xsl:value-of select="substring-before(substring-after(.,'ch '), ' ')"/><xsl:text>; </xsl:text>
+                                    <xsl:text>border-color:</xsl:text><xsl:value-of select="substring-after(substring-after(.,'ch '), ' ')"/><xsl:text>; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test="contains(., 'cm')">
+                                    <xsl:text>border-width:</xsl:text><xsl:value-of select="substring-before(.,' ')"/><xsl:text>; </xsl:text>
+                                    <xsl:text>border-style:</xsl:text><xsl:value-of select="substring-before(substring-after(.,'cm '), ' ')"/><xsl:text>; </xsl:text>
+                                    <xsl:text>border-color:</xsl:text><xsl:value-of select="substring-after(substring-after(.,'cm '), ' ')"/><xsl:text>; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test="contains(., 'pt')">
+                                    <xsl:text>border-width:</xsl:text><xsl:value-of select="substring-before(.,' ')"/><xsl:text>; </xsl:text>
+                                    <xsl:text>border-style:</xsl:text><xsl:value-of select="substring-before(substring-after(.,'pt '), ' ')"/><xsl:text>; </xsl:text>
+                                    <xsl:text>border-color:</xsl:text><xsl:value-of select="substring-after(substring-after(.,'pt '), ' ')"/><xsl:text>; </xsl:text>
+                                </xsl:when>
+                            </xsl:choose>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:border-top"'>
+                            <xsl:text>border-top: </xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:border-bottom"'>
+                            <xsl:text>border-bottom: </xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:border-left"'>
+                            <xsl:text>border-left: </xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:border-right"'>
+                            <xsl:text>border-right: </xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:column-width"'>
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="substring-before(.,'ch')"/><xsl:text>; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:row-height"'>
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>height:</xsl:text><xsl:value-of select="substring-before(.,'ch')"/><xsl:text>; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>height:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:width"'>
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="substring-before(.,'ch')"/><xsl:text>; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+<!--2DO END: change measurement equally -->
+                        <xsl:when test='name(.)="fo:font-style"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:font-name"'>
+                            <xsl:text>font-family:</xsl:text>
+                                <xsl:variable name="content" select="."/>
+                                <xsl:value-of select="$office:font-decls/style:font-decl[@style:name=$content]/@fo:font-family"/>
+                            <xsl:text>; </xsl:text>
+                            <xsl:if test="contains($office:font-decls/style:font-decl[@style:name=$content]/@style:font-style-name, 'Italic')">
+                                <xsl:text>font-style:italic; </xsl:text>
+                            </xsl:if>
+                            <xsl:if test="contains($office:font-decls/style:font-decl[@style:name=$content]/@style:font-style-name, 'Bold')">
+                                <xsl:text>font-weight:bold; </xsl:text>
+                            </xsl:if>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:font-weight"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:font-size"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:font-family"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:color"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:margin-left"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:margin-right"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:margin-top"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:margin-bottom"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:line-height"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:text-align"'>
+                            <!-- IMPORTANT is necessary as table cell value alignment is decided by runtime over the valuetype
+                                 Otherwise a table cell style-class will ALWAYS be overwritten by the run-time value -->
+                             <xsl:choose>
+                                <xsl:when test="contains(., 'start')">
+                                    <xsl:text>text-align:left ! important; </xsl:text>
+                                </xsl:when>
+                                <xsl:when test="contains(., 'end')">
+                                    <xsl:text>text-align:right ! important; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>text-align:</xsl:text><xsl:value-of select='.'/><xsl:text> ! important; </xsl:text>
+                                </xsl:otherwise>
+                             </xsl:choose>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:text-indent"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:text-background-color"'>
+                            <xsl:text>background-color:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="fo:background-color"'>
+                            <xsl:text>background-color:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:background-image"'>
+                            <xsl:text>background-image:url(</xsl:text><xsl:value-of select="@xlink:href"/><xsl:text>); </xsl:text>
+                            <xsl:choose>
+                                <xsl:when test="@style:repeat = 'repeat'">
+                                    <xsl:text>background-repeat:repeat; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>background-repeat:no-repeat; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                        <!-- text-shadow is a CSS2 feature and yet not common used in user-agents -->
+                        <xsl:when test='name(.)="fo:text-shadow"'>
+                            <xsl:value-of select="substring-after(name(.), ':')"/><xsl:text>:</xsl:text><xsl:value-of select="."/><xsl:text>; </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:text-crossing-out"'>
+                            <xsl:if test='not(.="none")'>
+                                <xsl:text>text-decoration:line-through; </xsl:text>
+                            </xsl:if>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:text-underline"'>
+                            <xsl:if test='not(.="none")'>
+                                <xsl:text>text-decoration:underline; </xsl:text>
+                            </xsl:if>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:text-position"'>
+                            <xsl:if test='contains(., "sub")'>
+                                <xsl:text>vertical-align:sub; </xsl:text>
+                            </xsl:if>
+                            <xsl:if test='contains(., "sup")'>
+                                <xsl:text>vertical-align:sup; </xsl:text>
+                            </xsl:if>
+                        </xsl:when>
+                        <!-- isDebugModeMESSAGE:
+                        <xsl:otherwise>
+                                <xsl:message>No transformation implemented for attribute-typ <xsl:value-of select="name(.)"/></xsl:message>
+                        </xsl:otherwise>-->
+                    </xsl:choose>
+                </xsl:for-each>
+            </xsl:when>
+            <!--+++++ PALM 3.2 SUBSET AND WAP PROPERTIES  +++++-->
+            <xsl:otherwise>
+                <xsl:for-each select="$styleAttributePath">
+                    <!-- isDebugModeMESSAGE:
+                    <xsl:message> Name:<xsl:value-of select="name()"/> Value:<xsl:value-of select="."/></xsl:message>      -->
+
+                    <!-- BUG WORK AROUND:
+                    Due to a bug in the XT Processor, it is not possible to create serveral elements in variable and search over them,
+                    after explicit conversion to nodeset
+                    This generated sting identifier shall be later changed back to a set of elements
+                    -->
+                    <xsl:choose>
+                        <!--*** FORMAT ATTRIBUTES ***-->
+
+                        <!-- Italic -->
+                        <xsl:when test='name(.)="fo:font-style"'>
+                            <xsl:if test="contains(., 'italic') or contains(., 'oblique')">
+                                <xsl:text>italic, </xsl:text>
+                            </xsl:if>
+                        </xsl:when>
+
+                        <!-- Boldface -->
+                        <xsl:when test='name(.)="fo:font-weight"'>
+                            <xsl:if test="contains(., 'bold') or contains(., 'bolder')">
+                                <xsl:text>bold, </xsl:text>
+                            </xsl:if>
+                        </xsl:when>
+
+                        <!-- Underline -->
+                        <xsl:when test='name(.)="style:text-underline"'>
+                            <xsl:text>underline, </xsl:text>
+                        </xsl:when>
+
+                        <!-- Alignment -->
+                        <xsl:when test='name(.)="fo:text-align"'>
+                             <xsl:choose>
+                                <xsl:when test="contains(., 'start')">
+                                    <xsl:text>align:left, </xsl:text>
+                                </xsl:when>
+                                <xsl:when test="contains(., 'end')">
+                                    <xsl:text>align:right, </xsl:text>
+                                </xsl:when>
+                                <xsl:when test="contains(., 'center')">
+                                    <xsl:text>align:center, </xsl:text>
+                                </xsl:when>
+                             </xsl:choose>
+                        </xsl:when>
+
+                        <!-- strikethrough -->
+                        <xsl:when test='name(.)="style:text-crossing-out"'>
+                            <xsl:text>strike, </xsl:text>
+                        </xsl:when>
+
+                        <!-- Font - size (Palm: emulator transformed sizes to available set (e.g. 30 to (probably) 9)-->
+                        <xsl:when test='name(.)="fo:font-size"'>
+                            <xsl:text>size:</xsl:text><xsl:value-of select="."/><xsl:text>:size, </xsl:text>
+                        </xsl:when>
+
+                        <!-- Font - Color (PALM: but mostly only 2 available)
+                            black (#000000)
+                            gray (#808080)(rendered as dark gray)
+                            silver (#C0C0C0)(rendered as light gray)
+                            white (#FFFFFF)-->
+                        <xsl:when test='name(.)="fo:color"'>
+                            <xsl:choose>
+                                <xsl:when test="contains(. , '#FFFFFF') or contains(. , '#ffffff') or contains(. , 'white') or contains(. , 'WHITE')">
+                                    <xsl:text>color:#FFFFFF, </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>color:#000000, </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+
+
+                        <!--*** TABLE ATTRIBUTES ***-->
+                        <xsl:when test='name(.)="fo:font-size"'>
+                            <xsl:text>size:</xsl:text><xsl:value-of select="."/><xsl:text>:size, </xsl:text>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:column-width"'>
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="substring-before(.,'ch')"/><xsl:text>:width, </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="."/><xsl:text>:width; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:row-height"'>
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>height:</xsl:text><xsl:value-of select="substring-before(.,'ch')"/><xsl:text>:height; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>height:</xsl:text><xsl:value-of select="."/><xsl:text>:height; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                        <xsl:when test='name(.)="style:width"'> <!--earlier fo:width-->
+                            <xsl:choose>
+                                <!-- changing the distance measure: inch to in -->
+                                <xsl:when test="contains(., 'ch')">
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="substring-before(.,'ch')"/><xsl:text>:width; </xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:text>width:</xsl:text><xsl:value-of select="."/><xsl:text>:width; </xsl:text>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:when>
+                    </xsl:choose>
+                </xsl:for-each>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+<!-- 2DO: NAMING CONVENTION variable are written with '-' instead of case-sensitive writing -->
+
+
+
+    <!-- ***** MEASUREMENT CONVERSIONS *****
+
+     * 1 centimeter = 10 mm
+
+     * 1 inch = 25.4 mm
+        While the English have already seen the light (read: the metric system), the US
+        remains loyal to this medieval system.
+
+     * 1 didot point = 0.376065 mm
+            The didot system originated in France but was used in most of Europe
+
+     * 1 pica point = 0.35146 mm
+            The Pica points system was developed in England and is used in Great-Britain and the US.
+
+     * 1 PostScript point = 0.35277138 mm
+            When Adobe created PostScript, they added their own system of points.
+            There are exactly 72 PostScript points in 1 inch.
+
+     * 1 pixel = 0.26458333.. mm   (by 96 dpi)
+            Most pictures have the 96 dpi resolution, but the dpi variable may vary by stylesheet parameter
+    -->
+
+
+    <!-- changing measure to mm -->
+    <xsl:template name="convert2mm">
+        <xsl:param name="value"/>
+
+        <xsl:param name="centimeter-in-mm"          select="10"/>
+        <xsl:param name="inch-in-mm"                select="25.4"/>
+        <xsl:param name="didot-point-in-mm"         select="0.376065"/>
+        <xsl:param name="pica-point-in-mm"          select="0.35146"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($value, 'cm')">
+                <xsl:value-of select="round(number(substring-before($value,'cm' )) * $centimeter-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'in')">
+                <xsl:value-of select="round(number(substring-before($value,'in' )) * $inch-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'dpt')">
+                <xsl:value-of select="round(number(substring-before($value,'dpt')) * $didot-point-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'ppt')">
+                <xsl:value-of select="round(number(substring-before($value,'ppt')) * $pica-point-in-mm)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$value"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- changing measure to cm -->
+    <xsl:template name="convert2cm">
+        <xsl:param name="value"/>
+
+        <xsl:param name="centimeter-in-mm"          select="10"/>
+        <xsl:param name="inch-in-mm"                select="25.4"/>
+        <xsl:param name="didot-point-in-mm"         select="0.376065"/>
+        <xsl:param name="pica-point-in-mm"          select="0.35146"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($value, 'mm')">
+                <xsl:value-of select="round(number(substring-before($value, 'mm')) div $centimeter-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'in')">
+                <xsl:value-of select="round(number(substring-before($value, 'in')) div $centimeter-in-mm * $inch-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'dpt')">
+                <xsl:value-of select="round(number(substring-before($value,'dpt')) div $centimeter-in-mm * $didot-point-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'ppt')">
+                <xsl:value-of select="round(number(substring-before($value,'ppt')) div $centimeter-in-mm * $pica-point-in-mm)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$value"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <!-- changing measure to inch (cp. section comment) -->
+    <xsl:template name="convert2inch">
+        <xsl:param name="value"/>
+
+        <xsl:param name="centimeter-in-mm"          select="10"/>
+        <xsl:param name="inch-in-mm"                select="25.4"/>
+        <xsl:param name="didot-point-in-mm"         select="0.376065"/>
+        <xsl:param name="pica-point-in-mm"          select="0.35146"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($value, 'mm')">
+                <xsl:value-of select="round(number(substring-before($value, 'mm')) div $inch-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'cm')">
+                <xsl:value-of select="round(number(substring-before($value, 'cm')) div $inch-in-mm * $centimeter-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'dpt')">
+                <xsl:value-of select="round(number(substring-before($value,'dpt')) div $inch-in-mm * $didot-point-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'ppt')">
+                <xsl:value-of select="round(number(substring-before($value,'ppt')) div $inch-in-mm * $pica-point-in-mm)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$value"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- changing measure to dpt (cp. section comment) -->
+    <xsl:template name="convert2dpt">
+        <xsl:param name="value"/>
+
+        <xsl:param name="centimeter-in-mm"          select="10"/>
+        <xsl:param name="inch-in-mm"                select="25.4"/>
+        <xsl:param name="didot-point-in-mm"         select="0.376065"/>
+        <xsl:param name="pica-point-in-mm"          select="0.35146"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($value, 'mm')">
+                <xsl:value-of select="round(number(substring-before($value, 'mm')) div $didot-point-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'cm')">
+                <xsl:value-of select="round(number(substring-before($value, 'cm')) div $didot-point-in-mm * $centimeter-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'in')">
+                <xsl:value-of select="round(number(substring-before($value, 'in')) div $didot-point-in-mm * $inch-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'ppt')">
+                <xsl:value-of select="round(number(substring-before($value,'ppt')) div $didot-point-in-mm * $pica-point-in-mm)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$value"/>
+            </xsl:otherwise>
+        </xsl:choose>
+
+    </xsl:template>
+
+
+    <!-- changing measure to ppt (cp. section comment) -->
+    <xsl:template name="convert2ppt">
+        <xsl:param name="value"/>
+
+        <xsl:param name="centimeter-in-mm"          select="10"/>
+        <xsl:param name="inch-in-mm"                select="25.4"/>
+        <xsl:param name="didot-point-in-mm"         select="0.376065"/>
+        <xsl:param name="pica-point-in-mm"          select="0.35146"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($value, 'mm')">
+                <xsl:value-of select="round(number(substring-before($value, 'mm')) div $pica-point-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'cm')">
+                <xsl:value-of select="round(number(substring-before($value, 'cm')) div $pica-point-in-mm * $centimeter-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'in')">
+                <xsl:value-of select="round(number(substring-before($value, 'in')) div $pica-point-in-mm * $inch-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'dpt')">
+                <xsl:value-of select="round(number(substring-before($value,'dpt')) div $pica-point-in-mm * $didot-point-in-mm)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$value"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- changing measure to pixel by via parameter provided dpi (dots per inch) standard factor (cp. section comment) -->
+    <xsl:template name="convert2pixel">
+        <xsl:param name="value"/>
+
+        <xsl:param name="centimeter-in-mm"          select="10"/>
+        <xsl:param name="inch-in-mm"                select="25.4"/>
+        <xsl:param name="didot-point-in-mm"         select="0.376065"/>
+        <xsl:param name="pica-point-in-mm"          select="0.35146"/>
+        <xsl:param name="pixel-in-mm"               select="$inch-in-mm div $dpi"/>
+
+        <xsl:choose>
+            <xsl:when test="contains($value, 'mm')">
+                <xsl:value-of select="round(number(substring-before($value, 'mm')) div $pixel-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'cm')">
+                <xsl:value-of select="round(number(substring-before($value, 'cm')) div $pixel-in-mm * $centimeter-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'in')">
+                <xsl:value-of select="round(number(substring-before($value, 'in')) div $pixel-in-mm * $inch-in-mm)"/>
+            </xsl:when>
+            <xsl:when test="contains($value, 'dpt')">
+                <xsl:value-of select="round(number(substring-before($value,'dpt')) div $pixel-in-mm * $didot-point-in-mm)"/>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$value"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/table.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/table.xsl
new file mode 100644 (file)
index 0000000..36339ed
--- /dev/null
@@ -0,0 +1,328 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+    <!-- table row handling -->
+    <xsl:include href="table_rows.xsl"/>
+    <!-- table column handling -->
+    <xsl:include href="table_columns.xsl"/>
+    <!-- table cell handling -->
+    <xsl:include href="table_cells.xsl"/>
+
+
+
+    <!-- ******************* -->
+    <!-- *** main table  *** -->
+    <!-- ******************* -->
+
+    <xsl:template match="table:table | table:sub-table">
+        <xsl:param name="collectedGlobalData"/>
+
+        <!-- a table will only be created if the "scenario" is active -->
+        <xsl:if test="string-length(table:scenario/@table:is-active) = 0">
+            <!-- collecting all visible "table:table-row" elements of the table -->
+            <xsl:variable name="allVisibleTableRows" select="table:table-row[not(@table:visibility = 'collapse' or @table:visibility = 'filter')]
+                    |    table:table-header-rows/descendant::table:table-row[not(@table:visibility = 'collapse' or @table:visibility = 'filter')]
+                    |    table:table-row-group/descendant::table:table-row[not(@table:visibility = 'collapse' or @table:visibility = 'filter')]"/>
+            <xsl:choose>
+                <!-- for all but WAP/WML devices a table border check is done (cp. "check-for-table-border") -->
+                <xsl:when test="not($outputType = 'WML')">
+
+                    <!-- As the alignment of a table is by 'align' attribut is deprecated and as the CSS 'float' attribute not well displayed,
+                         we do a little trick by encapsulating the table with a aligned 'div' element-->
+                    <xsl:variable name="table-alignment" select="$office:automatic-styles/style:style[@style:name = current()/@table:style-name]/style:properties/@table:align"/>
+
+                    <xsl:choose>
+                        <xsl:when test="string-length($table-alignment) != 0">
+                            <xsl:element name="div">
+                                <xsl:attribute name="align">
+                                    <xsl:choose>
+                                        <xsl:when test='$table-alignment="left" or $table-alignment="margins"'>
+                                                <xsl:text>left</xsl:text>
+                                        </xsl:when>
+                                        <xsl:when test='$table-alignment="right"'>
+                                            <xsl:text>right</xsl:text>
+                                        </xsl:when>
+                                        <xsl:when test='$table-alignment="center"'>
+                                            <xsl:text>center</xsl:text>
+                                        </xsl:when>
+                                    </xsl:choose>
+                                </xsl:attribute>
+                                <xsl:element name="table">
+
+                                    <xsl:apply-templates select="@table:style-name">
+                                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    </xsl:apply-templates>
+
+                                    <!-- workaround, set table border attribut if any cell-border exists
+                                    <xsl:call-template name="check-for-table-border">
+                                        <xsl:with-param name="allVisibleTableRows" select="$allVisibleTableRows"/>
+                                    </xsl:call-template> -->
+                                    <xsl:call-template name="create-column-style-variable">
+                                        <xsl:with-param name="allVisibleTableRows" select="$allVisibleTableRows"/>
+                                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                    </xsl:call-template>
+                                </xsl:element>
+                            </xsl:element>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:element name="table">
+                                <xsl:apply-templates select="@table:style-name">
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:apply-templates>
+
+                                <!-- workaround, set table border attribut if any cell-border exists
+                                <xsl:call-template name="check-for-table-border">
+                                    <xsl:with-param name="allVisibleTableRows" select="$allVisibleTableRows"/>
+                                </xsl:call-template>  -->
+                                <xsl:call-template name="create-column-style-variable">
+                                    <xsl:with-param name="allVisibleTableRows" select="$allVisibleTableRows"/>
+                                     <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:call-template>
+                            </xsl:element>
+                        </xsl:otherwise>
+                    </xsl:choose>
+
+                </xsl:when>
+                <xsl:otherwise>
+                <!-- for WML devices only ASCII table are written as tables are not implemented widley.
+                     Beginning from 'repeat-write-row' the templates are handled by the table_wml.xsl stylesheet -->
+                    <xsl:call-template name="create-column-style-variable">
+                        <xsl:with-param name="collectedGlobalData"   select="$collectedGlobalData"/>
+                        <xsl:with-param name="allVisibleTableRows"  select="$allVisibleTableRows"/>
+                    </xsl:call-template>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:if>
+    </xsl:template>
+
+
+
+    <xsl:template name="create-column-style-variable">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allVisibleTableRows"/>
+
+        <!-- all columns of the table -->
+        <xsl:variable name="allTableColumns" select="table:table-column |
+                                                     table:table-column-group/descendant::table:table-column |
+                                                     table:table-header-columns/descendant::table:table-column"/>
+        <!-- allColumnStyleEntries: Containing all columns of the table, hidden and viewed.
+            - if a column is hidden, it contains the hidden attribute, otherwise the style-properties will be stored
+            - if a column is being repeated, each repeated column is explicitly written as entry in this variable.
+              Later (during template "write-cell") the style of the column will be mixed with the cell-style by using
+              the position() of the column entry and comparing it with the iterating cell number. -->
+        <xsl:variable name="allColumnStyleEntries-RTF">
+            <xsl:call-template name="adding-column-styles-entries">
+                <xsl:with-param name="collectedGlobalData"   select="$collectedGlobalData"/>
+                <xsl:with-param name="allTableColumns"      select="$allTableColumns"/>
+            </xsl:call-template>
+        </xsl:variable>
+
+        <xsl:choose>
+            <xsl:when test="function-available('xt:node-set')">
+                <xsl:call-template name="create-table">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                    <xsl:with-param name="allVisibleTableRows"      select="$allVisibleTableRows"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="xt:node-set($allColumnStyleEntries-RTF)"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('xalan:nodeset')">
+                <xsl:call-template name="create-table">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                    <xsl:with-param name="allVisibleTableRows"      select="$allVisibleTableRows"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="xalan:nodeset($allColumnStyleEntries-RTF)"/>
+                </xsl:call-template>
+            </xsl:when>
+        </xsl:choose>
+
+    </xsl:template>
+
+
+
+    <xsl:template name="create-table">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allVisibleTableRows"/>
+        <xsl:param name="allColumnStyleEntries"/>
+
+
+        <!-- Some Office Calc documents simulate a background by repeating the last cell until end of space
+             (The value of "table:number-columns-repeated" is enourmous). Writing out all these cells would be fatal.
+             Therefore, this global variable shows us the longest row with content.
+
+        Earlier only the viewable columns were listed, but it is easier to handle with all columns:
+        <xsl:variable name="maxRowLength" select="count($allColumnStyleEntries/column-style-entry[not(@column-hidden-flag)])"/> -->
+        <xsl:variable name="maxRowLength" select="count($allColumnStyleEntries/column-style-entry)"/>
+
+
+        <!--isDebugMode-START-->
+        <xsl:if test="$isDebugMode">
+            <xsl:message>maxRowLength: <xsl:value-of select="$maxRowLength"/></xsl:message>
+            <xsl:variable name="numberOfHiddenColumns" select="count($allColumnStyleEntries/column-style-entry[@column-hidden-flag])"/>
+            <xsl:message>numberOfHiddenColumns: <xsl:value-of select="$numberOfHiddenColumns"/></xsl:message>
+            <xsl:call-template name="table-debug-allColumnStyleEntries">
+                <xsl:with-param name="allColumnStyleEntries" select="$allColumnStyleEntries"/>
+            </xsl:call-template>
+        </xsl:if>
+        <!--isDebugMode-END-->
+        <xsl:choose>
+            <xsl:when test="$outputType = 'WML'">
+                <!-- matching all rows - we can not use xsl:apply-template with a node-set parameter as by a bug in XT (James Clark)
+                     (here: allColumnStyleEntries) will be interpreted as a result tree fragment, where no search expression (XPath) can be used
+                     2DO:CHECK WITH XALAN-->
+                <xsl:for-each select="$allVisibleTableRows">
+                    <xsl:call-template name="wml-repeat-write-row">
+                        <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                        <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                        <xsl:with-param name="number-rows-repeated"     select="@table:number-rows-repeated"/>
+                        <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    </xsl:call-template>
+                </xsl:for-each>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- matching all rows - we can not use xsl:apply-template with a node-set parameter as by a bug in XT (James Clark)
+                     (here: allColumnStyleEntries) will be interpreted as a result tree fragment, where no search expression (XPath) can be used
+                     2DO:CHECK WITH XALAN -->
+                <xsl:for-each select="$allVisibleTableRows">
+                    <xsl:call-template name="repeat-write-row">
+                        <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                        <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                        <xsl:with-param name="number-rows-repeated"     select="@table:number-rows-repeated"/>
+                        <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    </xsl:call-template>
+                </xsl:for-each>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+
+
+    <!-- **************************** -->
+    <!-- *** HELPER: table border *** -->
+    <!-- **************************** -->
+
+    <!-- only one table border for HTML4 or CSS devices which contain one or more 'fo:border-top' attributes (pars pro toto, if one exist the other usually exist, too) -->
+    <!-- this was a work-around for the netscape 4.xx but not longer necessary for Mozilla -->
+    <xsl:template name="check-for-table-border">
+        <xsl:param name="allVisibleTableRows"/>
+
+        <xsl:variable name="startTime">
+            <xsl:if test="$isDebugMode and not($isJavaDisabled)">
+                <xsl:choose>
+                    <xsl:when test="function-available('system:current-time-millis')">
+                        <xsl:value-of select="system:current-time-millis()"/>
+                    </xsl:when>
+                    <xsl:when test="function-available('java:java.lang.System.currentTimeMillis')">
+                        <xsl:value-of select="java:java.lang.System.currentTimeMillis()"/>
+                    </xsl:when>
+                </xsl:choose>
+            </xsl:if>
+        </xsl:variable>
+
+        <!-- checks if one cell (table:table-cell) of the rows of this table (allVisibleTableRows) contains a border style (i.e. fo:border-top)
+             If only one single border element exist, the whole table will gets pre-defined borders (simple heuristic for better browser display) -->
+        <xsl:if test="$allVisibleTableRows/table:table-cell[@table:style-name=/*/*/style:style[style:properties/@fo:border-top]/@style:name]">
+            <xsl:attribute name="border">1</xsl:attribute>
+            <xsl:attribute name="bordercolor">#000000</xsl:attribute>
+            <xsl:attribute name="cellpadding">2</xsl:attribute>
+            <xsl:attribute name="cellspacing">0</xsl:attribute>
+            <xsl:attribute name="page-break-inside">page-break-inside:avoid</xsl:attribute>
+        </xsl:if>
+
+
+        <!-- check the time for borderchecking (debug)-->
+        <xsl:if test="$isDebugMode and not($isJavaDisabled)">
+            <xsl:variable name="endTime">
+                <xsl:choose>
+                    <xsl:when test="function-available('system:current-time-millis')">
+                        <xsl:value-of select="system:current-time-millis()"/>
+                    </xsl:when>
+                    <xsl:when test="function-available('java:java.lang.System.currentTimeMillis')">
+                        <xsl:value-of select="java:java.lang.System.currentTimeMillis()"/>
+                    </xsl:when>
+                </xsl:choose>
+            </xsl:variable>
+            <xsl:message>Time for checking BorderStyle: <xsl:value-of select="($endTime - $startTime)"/> ms</xsl:message>
+        </xsl:if>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/table_cells.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/table_cells.xsl
new file mode 100644 (file)
index 0000000..4671ea9
--- /dev/null
@@ -0,0 +1,484 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+    <!-- *********************************** -->
+    <!-- *** write repeating table cells *** -->
+    <!-- *********************************** -->
+
+
+    <!-- matching cells to give out -> covered table cells are not written out -->
+    <xsl:template match="table:table-cell">
+        <xsl:param name="collectedGlobalData"/>
+        <!-- position of the current input cell to get the correct colum style (hidden are also counted)-->
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="maxRowLength"/>
+
+        <xsl:if test="$isDebugMode">
+            <xsl:message>
+--------------> table:table-cell has been entered with node value: <xsl:value-of select="."/></xsl:message>
+            <xsl:message>table:number-columns-repeated: -<xsl:value-of select="@table:number-columns-repeated"/>-</xsl:message>
+        </xsl:if>
+
+        <xsl:call-template name="create-column-position-variable">
+            <!-- position of the current input cell to get the correct colum style (hidden are also counted)-->
+            <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+            <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+            <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+        </xsl:call-template>
+
+    </xsl:template>
+
+
+
+    <xsl:template name="create-column-position-variable">
+        <!-- position of the current input cell to get the correct colum style (hidden are also counted)-->
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="maxRowLength"/>
+
+        <!-- column position needed for styles, esp. for column-hidden-flag -->
+        <xsl:variable name="preceding-columns">
+            <xsl:for-each select="preceding-sibling::*">
+                <xsl:element name="quantity">
+                    <xsl:choose>
+                        <xsl:when test="string-length(@table:number-columns-repeated) = 0">1</xsl:when>
+                        <xsl:otherwise><xsl:value-of select="@table:number-columns-repeated"/></xsl:otherwise>
+                    </xsl:choose>
+                </xsl:element>
+            </xsl:for-each>
+        </xsl:variable>
+
+        <xsl:choose>
+            <xsl:when test="function-available('xt:node-set')">
+                <xsl:call-template name="create-table-cell">
+                    <!-- position of the current input cell to get the correct colum style (hidden are also counted)-->
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    <xsl:with-param name="column-position"          select="sum(xt:node-set($preceding-columns)/quantity) + 1"/>
+                    <xsl:with-param name="collectedGlobalData"      select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:when test="function-available('xalan:nodeset')">
+                <xsl:call-template name="create-table-cell">
+                    <!-- position of the current input cell to get the correct colum style (hidden are also counted)-->
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    <xsl:with-param name="column-position"          select="sum(xalan:nodeset($preceding-columns)/quantity) + 1"/>
+                    <xsl:with-param name="collectedGlobalData"      select="$collectedGlobalData"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:element name="NodeSetFunctionNotAvailable"/>
+                <xsl:call-template name="create-table-cell"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <xsl:template name="create-table-cell">
+        <!-- position of the current input cell to get the correct colum style (hidden are also counted)-->
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="maxRowLength"/>
+        <xsl:param name="column-position"/>
+
+
+        <xsl:if test="$isDebugMode">
+            <xsl:message>NEW VALUE: column-position: -<xsl:value-of select="$column-position"/>-</xsl:message>
+        </xsl:if>
+
+
+        <!-- a hidden column will give out nothing -->
+        <xsl:if test="not($allColumnStyleEntries/column-style-entry[position() = $column-position]/@column-hidden-flag)">
+            <xsl:choose>
+                <!-- when the columns are not repeated the next column-positions raises up to 1, otherwise up to the amount of repeated columns -->
+                <xsl:when test="@table:number-columns-repeated">
+                    <!-- writes multiple entries of a cell -->
+                    <xsl:call-template name="repeat-write-cell">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                        <xsl:with-param name="column-position"          select="$column-position"/>
+                        <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                        <xsl:with-param name="number-columns-repeated"  select="@table:number-columns-repeated"/>
+                    </xsl:call-template>
+                </xsl:when>
+                <xsl:otherwise>
+                    <!-- writes an entry of a cell -->
+                    <xsl:call-template name="write-cell">
+                        <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                        <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                        <xsl:with-param name="column-position"          select="$column-position"/>
+                        <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    </xsl:call-template>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:if>
+
+    </xsl:template>
+
+
+
+    <xsl:template name="repeat-write-cell">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="column-position"/>
+        <xsl:param name="maxRowLength"/>
+        <xsl:param name="number-columns-repeated"/>
+
+        <xsl:choose>
+            <!-- 2DO: This is the current workaround against the background simulation by an 'endless' repeating cell -->
+            <xsl:when test="$number-columns-repeated > 1 and $maxRowLength > $column-position">
+
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>+++++++++ starting cell writing +++++++++</xsl:message>
+                    <xsl:message>number-columns-repeated: -<xsl:value-of select="$number-columns-repeated"/>-</xsl:message>
+                    <xsl:message>maxRowLength: -<xsl:value-of select="$maxRowLength"/>-</xsl:message>
+                    <xsl:message>column-position: -<xsl:value-of select="$column-position"/>-</xsl:message>
+                </xsl:if>
+
+                <!-- writes an entry of a cell -->
+                <xsl:call-template name="write-cell">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="column-position"          select="$column-position"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                </xsl:call-template>
+                <!-- repeat calling this method until all elements written out -->
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>+++++++++ cell repetition +++++++++</xsl:message>
+                </xsl:if>
+                <xsl:call-template name="repeat-write-cell">
+                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="column-position"          select="$column-position + 1"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    <xsl:with-param name="number-columns-repeated"  select="$number-columns-repeated - 1"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- 2DO: This is the current workaround against the background simulation by an 'endless' repeating cell -->
+                <!--      When the maxRowLength is reached a last entry of a cell is written -->
+                <xsl:call-template name="write-cell">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="column-position"          select="$column-position"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <xsl:template name="write-cell">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="column-position"/>
+        <xsl:param name="maxRowLength"/>
+
+
+        <xsl:if test="$isDebugMode">
+            <xsl:message>WriteTest -> If nothing between '-' write cell -<xsl:value-of select="$allColumnStyleEntries/column-style-entry[position() = $column-position]/@column-hidden-flag"/>-</xsl:message>
+        </xsl:if>
+
+            <xsl:if test="$allColumnStyleEntries/column-style-entry[position() = $column-position]/@column-hidden-flag">
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>TABLE COLUMN is hidden!</xsl:message>
+                </xsl:if>
+            </xsl:if>
+
+        <xsl:choose>
+            <!-- a hidden column will give out nothing -->
+            <xsl:when test="$allColumnStyleEntries/column-style-entry[position() = $column-position]/@column-hidden-flag">
+                <xsl:if test="$isDebugMode">
+                    <xsl:message>TABLE COLUMN is hidden!</xsl:message>
+                </xsl:if>
+            </xsl:when>
+
+            <!-- NOT a hidden column -->
+            <xsl:otherwise>
+
+                <!-- a table is a table header, when it has a "table:table-header-rows" ancestor -->
+                <xsl:variable name="tableDataType">
+                    <xsl:choose>
+                        <xsl:when test="ancestor::table:table-header-rows">
+                            <xsl:text>th</xsl:text>
+                        </xsl:when>
+                        <xsl:otherwise>
+                            <xsl:text>td</xsl:text>
+                        </xsl:otherwise>
+                    </xsl:choose>
+                </xsl:variable>
+
+                <xsl:choose>
+                    <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+                    <xsl:when test="$outputType = 'CSS_HEADER'">
+                        <xsl:element name="{$tableDataType}">
+
+                            <xsl:if test="$isDebugMode">
+                                <xsl:message>
+*****************************************'<xsl:value-of select="$tableDataType"/>' element has been added!</xsl:message>
+                            </xsl:if>
+
+                            <xsl:if test="@table:number-columns-spanned">
+                                <xsl:attribute name="colspan">
+                                    <xsl:value-of select="@table:number-columns-spanned"/>
+                                </xsl:attribute>
+                            </xsl:if>
+                            <xsl:if test="@table:number-rows-spanned">
+                                <xsl:attribute name="rowspan">
+                                    <xsl:value-of select="@table:number-rows-spanned"/>
+                                </xsl:attribute>
+                            </xsl:if>
+
+
+
+                            <!-- *** the cell-style *** -->
+                            <!-- The cell style has no conclusion with the column style, so we switch the order/priorities due to browser issues
+
+                                The cell-style depends on two attributes:
+
+                                1) table:style-name - the style properties of cell. When they exist, a default alignement (cp. below) will be added for the
+                                                      case of no alignment in the style exist.
+
+                                2) table:value-type - the value type of the table-cell giving the default alignments.
+                                                      By default a string value is left aligned, all other are aligned:right.
+                            -->
+                            <xsl:choose>
+                                <xsl:when test="@table:style-name">
+                                    <xsl:attribute name="style">
+
+                                        <!-- CELL-STYLE: alignment by table:value-type (without existing table:style-name)-->
+                                        <xsl:variable name="cellStyle" select="$collectedGlobalData/allstyles/*[name()=current()/@table:style-name]"/>
+                                        <xsl:choose>
+                                            <xsl:when test="string-length($cellStyle) > 0 and not(contains($cellStyle, 'text-align'))">
+                                                <!-- CELL-STYLE: alignment by table:value-type -->
+                                                <!-- no alignment in the cell style, the alignment based on the table:value-type will be added -->
+                                                <xsl:choose>
+                                                    <xsl:when test="@table:value-type and not(@table:value-type = 'string')">
+                                                        <xsl:value-of select="concat($collectedGlobalData/allstyles/*[name()=current()/@table:style-name], 'text-align:right; ')"/>
+                                                    </xsl:when>
+                                                    <xsl:otherwise>
+                                                        <xsl:value-of select="concat($collectedGlobalData/allstyles/*[name()=current()/@table:style-name], 'text-align:left; ')"/>
+                                                    </xsl:otherwise>
+                                                </xsl:choose>
+                                            </xsl:when>
+                                            <xsl:otherwise>
+                                                <!-- CELL-STYLE: alignment by table:value-type -->
+                                                <!-- no CSS style properties exist, only alignment from the table:value-type will be used -->
+                                                <xsl:choose>
+                                                    <xsl:when test="@table:value-type and not(@table:value-type = 'string')">text-align:right; </xsl:when>
+                                                    <xsl:otherwise>text-align:left; </xsl:otherwise>
+                                                </xsl:choose>
+                                            </xsl:otherwise>
+                                        </xsl:choose>
+
+                                         <!-- column-style (disjunct of cell style -->
+                                         <!-- 2DO: only absolut styles are supported, relative styles (i.e. 'style:rel-column-width' e.g. with value "8933*" are ignored.
+                                              Issue: browsers (not sure if CSS) does not support the '*' relationship, only the '%', where the sum is always '100'!
+                                              For this, it is easier to work on with the absolute values, instead of calculating the values for 100% -->
+                                         <xsl:value-of select="$allColumnStyleEntries/column-style-entry[position()=$column-position]"/>
+                                    </xsl:attribute>
+                                    <!-- CELL-STYLE: table:style-name -->
+                                    <xsl:attribute name="class">
+                                        <xsl:value-of select="translate(@table:style-name, '. %()/\', '')"/>
+                                    </xsl:attribute>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <xsl:attribute name="style">
+                                        <!-- CELL-STYLE: alignment by table:value-type (without existing table:style-name)-->
+                                        <!-- no table:style-name exist, only alignment from the table:value-type will be used -->
+                                        <xsl:choose>
+                                            <xsl:when test="@table:value-type and not(@table:value-type = 'string')">
+                                                text-align:right;
+                                            </xsl:when>
+                                            <xsl:otherwise>
+                                                text-align:left;
+                                            </xsl:otherwise>
+                                        </xsl:choose>
+                                    </xsl:attribute>
+                                </xsl:otherwise>
+                            </xsl:choose>
+
+                            <xsl:choose>
+                                <!-- In case of no cell content a non-breakable space will be inserted
+                                     to make the browser show the table-cell grid -->
+                                <xsl:when test="not(child::text()) and not(child::*)">
+                                    <xsl:text> &#160;</xsl:text>
+                                </xsl:when>
+                                <xsl:otherwise>
+                                    <!-- *** the column-style *** -->
+                                    <!--  the column style has no conclusion with the cell style, so we switch the order/priorities due to browser issues-->
+                                    <xsl:element name="span">
+                                        <xsl:attribute name="class">
+                                            <xsl:value-of select="$allColumnStyleEntries/column-style-entry[position() = $column-position]/@style-name"/>
+                                        </xsl:attribute>
+                                        <xsl:apply-templates>
+                                            <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                        </xsl:apply-templates>
+                                    </xsl:element>
+                                </xsl:otherwise>
+                            </xsl:choose>
+                        </xsl:element>
+                    </xsl:when>
+
+                    <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+                    <xsl:when test="$outputType = 'CSS_INLINED'">
+                        <xsl:element name="{$tableDataType}">
+
+                            <xsl:if test="@table:number-columns-spanned">
+                                <xsl:attribute name="colspan">
+                                    <xsl:value-of select="@table:number-columns-spanned"/>
+                                </xsl:attribute>
+                            </xsl:if>
+                            <xsl:if test="@table:number-rows-spanned">
+                                <xsl:attribute name="rowspan">
+                                    <xsl:value-of select="@table:number-rows-spanned"/>
+                                </xsl:attribute>
+                            </xsl:if>
+
+                            <xsl:attribute name="style">
+                                <!-- cell-style -->
+                                <xsl:value-of select="$collectedGlobalData/allstyles/*[name()=current()/@table:style-name]"/>
+                                <!-- column-style -->
+                                <xsl:value-of select="$allColumnStyleEntries/column-style-entry[position()=$column-position]"/>
+                                <!-- TABLE:VALUE-TYPE - the value of a table-cell will be aligned left by default only exlicit non-string is aligned:right-->
+                                <xsl:choose>
+                                    <xsl:when test="@table:value-type and not(@table:value-type = 'string')">
+                                        <xsl:text>text-align:right;</xsl:text>
+                                    </xsl:when>
+                                    <xsl:otherwise>
+                                        <xsl:text>text-align:left;</xsl:text>
+                                    </xsl:otherwise>
+                                </xsl:choose>
+                            </xsl:attribute>
+
+                            <!-- &#160 is a non-breakable space, necessary to make to the browser show the table-cell grid -->
+                            <xsl:if test="not(child::text()) and not(child::*)"> &#160;</xsl:if>
+                            <xsl:apply-templates>
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:apply-templates>
+                        </xsl:element>
+                    </xsl:when>
+                    <!--+++++ PALM INLINED WAY  +++++-->
+                    <xsl:when test="$outputType = 'PALM'">
+                        <xsl:element name="{$tableDataType}">
+                            <xsl:if test="@table:number-columns-spanned">
+                                <xsl:attribute name="colspan">
+                                    <xsl:value-of select="@table:number-columns-spanned"/>
+                                </xsl:attribute>
+                            </xsl:if>
+
+                            <xsl:if test="@table:number-rows-spanned">
+                                <xsl:attribute name="rowspan">
+                                    <xsl:value-of select="@table:number-rows-spanned"/>
+                                </xsl:attribute>
+                            </xsl:if>
+                            <xsl:apply-templates>
+                                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                            </xsl:apply-templates>
+                        </xsl:element>
+                    </xsl:when>
+                    <!--+++++ WML WAY  +++++-->
+                    <xsl:when test="$outputType = 'WML'">
+                        <xsl:choose>
+                            <xsl:when test="not($allColumnStyleEntries/column-style-entry[last() = $column-position])">
+                                <xsl:apply-templates>
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:apply-templates>
+                                <xsl:text>, </xsl:text>
+                            </xsl:when>
+                            <xsl:otherwise>
+                                <xsl:apply-templates>
+                                    <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+                                </xsl:apply-templates>
+                                <xsl:text>; </xsl:text>
+                                <xsl:element name="br"/>
+                            </xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:when>
+                </xsl:choose>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/table_columns.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/table_columns.xsl
new file mode 100644 (file)
index 0000000..a9a907f
--- /dev/null
@@ -0,0 +1,215 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+    <!-- ******************************************** -->
+    <!-- *** Create table columns style variable  *** -->
+    <!-- ******************************************** -->
+
+    <xsl:template name="adding-column-styles-entries">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allTableColumns"/>
+
+        <xsl:for-each select="$allTableColumns">
+
+            <xsl:variable name="column-style-entry" select="$collectedGlobalData/allstyles/*[name() = translate(current()/@table:style-name, '. %()/\', '')]"/>
+            <xsl:choose>
+                <xsl:when test="not(@table:number-columns-repeated)">
+                    <!-- writes an entry of a column in the columns-variable -->
+                    <xsl:call-template name="adding-column-style-entry">
+                        <xsl:with-param name="column-style-entry" select="$column-style-entry"/>
+                    </xsl:call-template>
+                </xsl:when>
+                <!-- No higher repetition of cells greater than 4 for the last and second last column -->
+                <!-- a hack for the sample document 'Waehrungsumrechner.sxc having 230 repeated columns in the second last column -->
+                <!-- ??? <xsl:when test="(position() = last() or (position() = (last() - 1)) and @table:number-columns-repeated &lt; 5)"> ???-->
+                <xsl:when test="position() = last() or position() = (last() - 1)">
+                    <xsl:if test="@table:number-columns-repeated &lt; 5">
+                        <!-- writes an entry of a column in the columns-variable -->
+                        <xsl:call-template name="repeat-adding-column-style-entry">
+                            <xsl:with-param name="column-style-entry"       select="$column-style-entry"/>
+                            <xsl:with-param name="number-columns-repeated"  select="1"/>
+                        </xsl:call-template>
+                    </xsl:if>
+                </xsl:when>
+                <xsl:otherwise>
+                    <!-- repeated colums will be written explicit several times in the variable-->
+                    <xsl:call-template name="repeat-adding-column-style-entry">
+                        <xsl:with-param name="column-style-entry"           select="$column-style-entry"/>
+                        <xsl:with-param name="number-columns-repeated"      select="@table:number-columns-repeated"/>
+                    </xsl:call-template>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:for-each>
+     </xsl:template>
+
+
+    <!-- WRITES THE REPEATED COLUMN STYLE EXPLICIT AS AN ELEMENT IN THE COLUMNS-VARIABLE -->
+    <xsl:template name="repeat-adding-column-style-entry">
+        <xsl:param name="column-style-entry"/>
+        <xsl:param name="number-columns-repeated"/>
+
+        <xsl:choose>
+            <xsl:when test="$number-columns-repeated > 1">
+                <!-- writes an entry of a column in the columns-variable -->
+                <xsl:call-template name="adding-column-style-entry">
+                    <xsl:with-param name="column-style-entry"   select="$column-style-entry"/>
+                </xsl:call-template>
+                <!-- repeat calling this method until all elements written out -->
+                <xsl:call-template name="repeat-adding-column-style-entry">
+                    <xsl:with-param name="column-style-entry"       select="$column-style-entry"/>
+                    <xsl:with-param name="number-columns-repeated"  select="$number-columns-repeated - 1"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- writes an entry of a column in the columns-variable -->
+                <xsl:call-template name="adding-column-style-entry">
+                    <xsl:with-param name="column-style-entry"   select="$column-style-entry"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+    <!-- THE COLUMN-STYLE WRITE-PATTERN FOR EACH COLUMN WRITTEN IN A VARIABLE -->
+    <xsl:template name="adding-column-style-entry">
+        <xsl:param name="column-style-entry"/>
+
+        <xsl:element name="column-style-entry">
+            <xsl:choose>
+                <xsl:when test="@table:visibility = 'collapse' or @table:visibility = 'filter'">
+                    <xsl:attribute name="column-hidden-flag">true</xsl:attribute>
+                </xsl:when>
+                <xsl:otherwise>
+                    <xsl:variable  name="table:style-name"         select="translate(@table:style-name, '. %()/\', '')"/>
+                    <xsl:attribute name="style-name"><xsl:value-of select="$table:style-name"/></xsl:attribute>
+                    <xsl:value-of select="$column-style-entry"/>
+                </xsl:otherwise>
+            </xsl:choose>
+        </xsl:element>
+    </xsl:template>
+
+
+
+    <!--isDebugMode-START-->
+    <!-- giving out the 'allColumnStyle' variable:
+        For each 'column-style-entry' of the 'allColumnStyleEntries' variable the style-name is given out.
+        In case of 'column-hidden-flag' attribute the text 'Column is hidden is given out.-->
+    <xsl:template name="table-debug-allColumnStyleEntries">
+        <xsl:param name="allColumnStyleEntries"/>
+
+        <!-- debug output as table summary attribut in html -->
+        <xsl:attribute name="summary">
+            <xsl:call-template name="table-debug-column-out">
+                <xsl:with-param name="allColumnStyleEntries" select="$allColumnStyleEntries"/>
+            </xsl:call-template>
+        </xsl:attribute>
+        <!-- debug output to console -->
+        <xsl:message>
+            <xsl:call-template name="table-debug-column-out">
+                <xsl:with-param name="allColumnStyleEntries" select="$allColumnStyleEntries"/>
+            </xsl:call-template>
+        </xsl:message>
+    </xsl:template>
+
+
+    <xsl:template name="table-debug-column-out">
+        <xsl:param name="allColumnStyleEntries"/>
+            <xsl:text>
+            DebugInformation: For each 'column-style-entry' of the 'allColumnStyleEntries' variable the style-name is given out.
+                              In case of 'column-hidden-flag' attribute the text 'column is hidden' is given out.
+            </xsl:text>
+                <xsl:for-each select="$allColumnStyleEntries/column-style-entry">
+                <xsl:choose>
+                <xsl:when test="@column-hidden-flag">
+            <xsl:text>  </xsl:text><xsl:value-of select="@style-name"/><xsl:text>column is hidden</xsl:text><xsl:text>
+            </xsl:text>
+                </xsl:when>
+                <xsl:otherwise>
+            <xsl:text>  </xsl:text><xsl:value-of select="@style-name"/><xsl:text> = </xsl:text><xsl:value-of select="."/><xsl:text>
+            </xsl:text>
+                </xsl:otherwise>
+                </xsl:choose>
+                           </xsl:for-each>
+    </xsl:template>
+    <!--isDebugMode-END-->
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/ooo/table_rows.xsl b/framework/mime_tmp/Horde/Mime/Viewer/ooo/table_rows.xsl
new file mode 100644 (file)
index 0000000..6f7d17d
--- /dev/null
@@ -0,0 +1,177 @@
+<!--
+
+   The Contents of this file are made available subject to the terms of
+   either of the following licenses
+
+          - GNU Lesser General Public License Version 2.1
+          - Sun Industry Standards Source License Version 1.1
+
+   Sun Microsystems Inc., October, 2000
+
+   GNU Lesser General Public License Version 2.1
+   =============================================
+   Copyright 2000 by Sun Microsystems, Inc.
+   901 San Antonio Road, Palo Alto, CA 94303, USA
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License version 2.1, as published by the Free Software Foundation.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA  02111-1307  USA
+
+
+   Sun Industry Standards Source License Version 1.1
+   =================================================
+   The contents of this file are subject to the Sun Industry Standards
+   Source License Version 1.1 (the "License"); You may not use this file
+   except in compliance with the License. You may obtain a copy of the
+   License at http://www.openoffice.org/license.html.
+
+   Software provided under this License is provided on an "AS IS" basis,
+   WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING,
+   WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
+   MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
+   See the License for the specific provisions governing your rights and
+   obligations concerning the Software.
+
+   The Initial Developer of the Original Code is: Sun Microsystems, Inc.
+
+   Copyright Â© 2002 by Sun Microsystems, Inc.
+
+   All Rights Reserved.
+
+   Contributor(s): _______________________________________
+
+-->
+<xsl:stylesheet version="1.0"
+                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:office="http://openoffice.org/2000/office"
+                xmlns:style="http://openoffice.org/2000/style"
+                xmlns:text="http://openoffice.org/2000/text"
+                xmlns:table="http://openoffice.org/2000/table"
+                xmlns:draw="http://openoffice.org/2000/drawing"
+                xmlns:fo="http://www.w3.org/1999/XSL/Format"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
+                xmlns:number="http://openoffice.org/2000/datastyle"
+                xmlns:svg="http://www.w3.org/2000/svg"
+                xmlns:chart="http://openoffice.org/2000/chart"
+                xmlns:dr3d="http://openoffice.org/2000/dr3d"
+                xmlns:math="http://www.w3.org/1998/Math/MathML"
+                xmlns:form="http://openoffice.org/2000/form"
+                xmlns:script="http://openoffice.org/2000/script"
+                office:class="text"
+                office:version="1.0"
+                xmlns:dc="http://purl.org/dc/elements/1.1/"
+                xmlns:meta="http://openoffice.org/2000/meta"
+                xmlns:config="http://openoffice.org/2001/config"
+                xmlns:help="http://openoffice.org/2000/help"
+                xmlns:xt="http://www.jclark.com/xt"
+                xmlns:system="http://www.jclark.com/xt/java/java.lang.System"
+                xmlns:xalan="http://xml.apache.org/xalan"
+                xmlns:java="http://xml.apache.org/xslt/java"
+                exclude-result-prefixes="java">
+
+
+
+    <!-- ********************************************* -->
+    <!-- *** write (explicit) repeating table rows *** -->
+    <!-- ********************************************* -->
+
+    <xsl:template name="repeat-write-row">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="number-rows-repeated" select="1"/>
+        <xsl:param name="maxRowLength"/>
+
+        <xsl:choose>
+            <!-- write an entry of a row and repeat calling this method until all elements are written out -->
+            <xsl:when test="$number-rows-repeated > 1 and (table:table-cell/text() or table:table-cell/*)">
+                <xsl:call-template name="write-row">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                </xsl:call-template>
+
+                <!-- 2DO: take variable from the output of repeated write-row and iterate giving out the variable -->
+                <xsl:call-template name="repeat-write-row">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                    <xsl:with-param name="number-rows-repeated"     select="$number-rows-repeated - 1"/>
+                </xsl:call-template>
+            </xsl:when>
+            <!-- write a single entry of a row -->
+            <xsl:otherwise>
+                <xsl:call-template name="write-row">
+                    <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                    <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                    <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+                </xsl:call-template>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+
+    <xsl:template name="write-row">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="maxRowLength"/>
+
+
+        <xsl:element name="tr">
+            <!-- writing the style of the row -->
+            <xsl:call-template name='add-style-properties'>
+                <xsl:with-param name="collectedGlobalData" select="$collectedGlobalData"/>
+            </xsl:call-template>
+
+            <xsl:if test="$isDebugMode">
+                <xsl:message>
+*************************'tr' element has been added!</xsl:message>
+            </xsl:if>
+
+            <xsl:apply-templates select="table:table-cell">
+                <xsl:with-param name="collectedGlobalData"       select="$collectedGlobalData"/>
+                <xsl:with-param name="allColumnStyleEntries"    select="$allColumnStyleEntries"/>
+                <xsl:with-param name="maxRowLength"             select="$maxRowLength"/>
+            </xsl:apply-templates>
+
+        </xsl:element>
+    </xsl:template>
+
+
+    <!-- **************************** -->
+    <!-- *** HELPER: table styles *** -->
+    <!-- **************************** -->
+
+    <xsl:template name="add-style-properties">
+        <xsl:param name="collectedGlobalData"/>
+        <xsl:param name="allColumnStyleEntries"/>
+        <xsl:param name="node-position"/>
+
+        <xsl:choose>
+            <!--+++++ CSS (CASCADING STLYE SHEET) HEADER STYLE WAY +++++-->
+            <xsl:when test="$outputType = 'CSS_HEADER'">
+                <xsl:attribute name="class">
+                    <xsl:value-of select="translate(@table:style-name, '. %()/\', '')"/>
+                </xsl:attribute>
+            </xsl:when>
+
+            <!--+++++ HTML 4.0 INLINED WAY  +++++-->
+            <xsl:when test="$outputType = 'CSS_INLINED'">
+                <xsl:attribute name="style">
+                    <xsl:value-of select="$collectedGlobalData/allstyles/*[name()=current()/@table:style-name]"/>
+                </xsl:attribute>
+            </xsl:when>
+        </xsl:choose>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/pdf.php b/framework/mime_tmp/Horde/Mime/Viewer/pdf.php
new file mode 100644 (file)
index 0000000..40e46ec
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+/**
+ * The Horde_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.
+ *
+ * Copyright 2003-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_pdf extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Return the content-type.
+     *
+     * @return string  The content-type of the output.
+     */
+    public function getType()
+    {
+        return 'application/pdf';
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/php.php b/framework/mime_tmp/Horde/Mime/Viewer/php.php
new file mode 100644 (file)
index 0000000..0c00954
--- /dev/null
@@ -0,0 +1,138 @@
+<?php
+
+require_once dirname(__FILE__) . '/source.php';
+
+/**
+ * The Horde_MIME_Viewer_php class renders out syntax-highlighted PHP code in
+ * HTML format.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_php extends Horde_MIME_Viewer_source
+{
+    /**
+     * Renders out the contents.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        ini_set('highlight.comment', 'comment');
+        ini_set('highlight.default', 'default');
+        ini_set('highlight.keyword', 'keyword');
+        ini_set('highlight.string', 'string');
+        ini_set('highlight.html', 'html');
+
+        $code = $this->mime_part->getContents();
+        if (strpos($code, '<?php') === false) {
+            $results = $this->lineNumber(str_replace('&lt;?php&nbsp;', '', highlight_string('<?php ' . $code, true)));
+        } else {
+            $results = $this->lineNumber(highlight_string($code, true));
+        }
+
+        // Educated guess at whether we are inline or not.
+        if (headers_sent() || ob_get_length()) {
+            return $results;
+        } else {
+            return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc')
+                . $results
+                . Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc');
+        }
+    }
+
+    /**
+     * Add line numbers to a block of code.
+     *
+     * @param string $code  The code to number.
+     */
+    public function lineNumber($code, $linebreak = "\n")
+    {
+        // Clean up.
+        $code = preg_replace(array('/<code><span style="color: #?\w+">\s*/',
+                                   '/<code><font color="#?\w+">\s*/',
+                                   '/\s*<\/span>\s*<\/span>\s*<\/code>/',
+                                   '/\s*<\/font>\s*<\/font>\s*<\/code>/'),
+                             '',
+                             $code);
+        $code = str_replace(array('&nbsp;',
+                                  '&amp;',
+                                  '<br />',
+                                  '<span style="color: ',
+                                  '<font color="',
+                                  '</font>',
+                            ),
+                            array(' ',
+                                  '&#38;',
+                                  "\n",
+                                  '<span class="',
+                                  '<span class="',
+                                  '</span>',
+                            ),
+                            $code);
+        $code = trim($code);
+
+        // Normalize newlines.
+        $code = str_replace("\r", '', $code);
+        $code = preg_replace('/\n\n\n+/', "\n\n", $code);
+
+        $lines = explode("\n", $code);
+
+        $results = array('<ol class="code-listing striped">');
+        $previous = false;
+        foreach ($lines as $lineno => $line) {
+            if (substr($line, 0, 7) == '</span>') {
+                $previous = false;
+                $line = substr($line, 7);
+            }
+
+            if (empty($line)) {
+                $line = '&#160;';
+            }
+
+            if ($previous) {
+                $line = "<span class=\"$previous\">" . $line;
+            }
+
+            // Save the previous style.
+            if (strpos($line, '<span') !== false) {
+                switch (substr($line, strrpos($line, '<span') + 13, 1)) {
+                case 'c':
+                    $previous = 'comment';
+                    break;
+
+                case 'd':
+                    $previous = 'default';
+                    break;
+
+                case 'k':
+                    $previous = 'keyword';
+                    break;
+
+                case 's':
+                    $previous = 'string';
+                    break;
+                }
+            }
+
+            // Unset previous style unless the span continues.
+            if (substr($line, -7) == '</span>') {
+                $previous = false;
+            } elseif ($previous) {
+                $line .= '</span>';
+            }
+
+            $results[] = '<li id="l' . ($lineno + 1). '">' . $line . '</li>';
+        }
+
+        $results[] = '</ol>';
+        return implode("\n", $results);
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/plain.php b/framework/mime_tmp/Horde/Mime/Viewer/plain.php
new file mode 100644 (file)
index 0000000..ad0aa17
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_plain class renders out plain text with URLs made
+ * into hyperlinks (if viewing inline).
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_plain extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Can this driver render various views?
+     *
+     * @var boolean
+     */
+    protected $_canrender = array(
+        'full' => true,
+        'info' => false,
+        'inline' => true,
+    );
+
+    /**
+     * Render the contents.
+     *
+     * @return array  TODO
+     */
+    protected function _render()
+    {
+        $text = $this->_mimepart->getContents();
+        $charset = $this->_mimepart->getCharset();
+
+        /* Check for 'flowed' text data. */
+        if ($this->_mimepart->getContentTypeParameter('format') == 'flowed') {
+            $text = $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp'));
+        }
+
+        require_once 'Horde/Text/Filter.php';
+        return array(
+            'data' => '<html><body><tt>' . Text_Filter::filter($text, 'text2html', array('parselevel' => TEXT_HTML_MICRO, 'charset' => $charset, 'class' => null)) . '</tt></body></html>',
+            'type' => 'text/html; charset=' . $charset;
+        );
+    }
+
+    /**
+     * Render the contents for inline viewing.
+     *
+     * @return string  The rendered contents.
+     */
+    protected function _renderInline()
+    {
+        $text = String::convertCharset($this->_mimepart->getContents(), $this->_mimepart->getCharset());
+
+        /* Check for 'flowed' text data. */
+        return ($this->_mimepart->getContentTypeParameter('format') == 'flowed')
+            ? $this->_formatFlowed($text, $this->_mimepart->getContentTypeParameter('delsp'))
+            : $text;
+    }
+
+    /**
+     * Format flowed text for HTML output.
+     *
+     * @param string $text    The text to format.
+     * @param boolean $delsp  Was text created with DelSp formatting?
+     *
+     * @return string  The formatted text.
+     */
+    protected function _formatFlowed($text, $delsp = null)
+    {
+        require_once 'Text/Flowed.php';
+        $flowed = new Text_Flowed($this->_mimepart->replaceEOL($text, "\n"), $this->_mimepart->getCharset());
+        $flowed->setMaxLength(0);
+        if (!is_null($delsp)) {
+            $flowed->setDelSp($delsp);
+        }
+        return $flowed->toFixed();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/rar.php b/framework/mime_tmp/Horde/Mime/Viewer/rar.php
new file mode 100644 (file)
index 0000000..5fe863a
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_rar class renders out the contents of .rar archives
+ * in HTML format.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @author  Michael Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_rar extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Rar compression methods.
+     *
+     * @var array
+     */
+    protected $_methods = array(
+        0x30 => 'Store',
+        0x31 => 'Fastest',
+        0x32 => 'Fast',
+        0x33 => 'Normal',
+        0x34 => 'Good',
+        0x35 => 'Best'
+    );
+
+    /**
+     * Render out the currently set contents using rar.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public 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';
+
+        $rarData = $this->getRarData($contents);
+        $fileCount = count($rarData);
+
+        $text  = '<strong>' . htmlspecialchars(sprintf(_("Contents of \"%s\""), $this->mime_part->getName())) . ':</strong>' . "\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(sprintf(ngettext("File Count: %d file", "File Count: %d files", $fileCount), $fileCount));
+        $text .= "\n\n";
+        $text .= Text::htmlAllSpaces(
+                     String::pad(_("File Name"),     50, ' ', STR_PAD_RIGHT) .
+                     String::pad(_("Attributes"),    10, ' ', STR_PAD_LEFT) .
+                     String::pad(_("Size"),          10, ' ', STR_PAD_LEFT) .
+                     String::pad(_("Modified Date"), 19, ' ', STR_PAD_LEFT) .
+                     String::pad(_("Method"),        10, ' ', STR_PAD_LEFT) .
+                     String::pad(_("Ratio"),         10, ' ', STR_PAD_LEFT)
+                 ) . "\n";
+
+        $text .= str_repeat('-', 109) . "\n";
+
+        foreach ($rarData as $val) {
+            $ratio = (empty($val['size'])) ? 0 : 100 * ($val['csize'] / $val['size']);
+            $text .= Text::htmlAllSpaces(
+                         String::pad($val['name'], 50, ' ', STR_PAD_RIGHT) .
+                         String::pad($val['attr'], 10, ' ', STR_PAD_LEFT) .
+                         String::pad($val['size'], 10, ' ', STR_PAD_LEFT) .
+                         String::pad(strftime("%d-%b-%Y %H:%M", $val['date']), 19, ' ', STR_PAD_LEFT) .
+                         String::pad($val['method'], 10, ' ', STR_PAD_LEFT) .
+                         String::pad(sprintf("%1.1f%%", $ratio), 10, ' ', 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.
+     *
+     * @return string  The MIME type of this part.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+
+    /**
+     * Checks to see if the data is a valid Rar archive.
+     *
+     * @param string &$data  The rar archive data.
+     *
+     * @return boolean  True if valid, false if invalid.
+     */
+    public 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.
+     *
+     * @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
+     */
+    public 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;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/report.php b/framework/mime_tmp/Horde/Mime/Viewer/report.php
new file mode 100644 (file)
index 0000000..89197e6
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_report class is a wrapper used to load the
+ * appropriate Horde_MIME_Viewer for multipart/report data (RFC 3462).
+ *
+ * Copyright 2003-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_report extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Stores the Horde_MIME_Viewer of the specified protocol.
+     *
+     * @var Horde_MIME_Viewer
+     */
+    protected $_viewer;
+
+    /**
+     * Render the multipart/report data.
+     *
+     * @param array $params  An array of parameters needed.
+     *
+     * @return string  The rendered data.
+     */
+    public function render($params = array())
+    {
+        /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */
+        if (!($this->_resolveViewer())) {
+            return;
+        }
+
+        /* Render using the loaded Horde_MIME_Viewer object. */
+        return $this->_viewer->render($params);
+    }
+
+    /**
+     * Returns the content-type of the Viewer used to view the part.
+     *
+     * @return string  A content-type string.
+     */
+    public function getType()
+    {
+        /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */
+        if (!($this->_resolveViewer())) {
+            return 'application/octet-stream';
+        } else {
+            return $this->_viewer->getType();
+        }
+    }
+
+    /**
+     * Load a Horde_MIME_Viewer according to the report-type parameter stored
+     * in the MIME_Part to render. If unsuccessful, try to load a generic
+     * multipart Horde_MIME_Viewer.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    protected function _resolveViewer()
+    {
+        $type = $viewer = null;
+
+        if (empty($this->_viewer)) {
+            if (($type = $this->mime_part->getContentTypeParameter('report-type'))) {
+                $viewer = &Horde_MIME_Viewer::factory($this->mime_part, 'message/' . String::lower($type));
+                $type = $this->mime_part->getPrimaryType();
+            } else {
+                /* If report-type is missing, the message is an improper
+                 * multipart/report message.  Attempt to fall back to a
+                 * multipart/mixed viewer instead. */
+                $type = 'multipart';
+            }
+
+            if (empty($viewer) ||
+                (String::lower(get_class($viewer)) == 'mime_viewer_default')) {
+                if (!($viewer = &Horde_MIME_Viewer::factory($this->mime_part, $type . '/*'))) {
+                    return false;
+                }
+            }
+            $this->_viewer = $viewer;
+        }
+
+        return true;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/rfc822.php b/framework/mime_tmp/Horde/Mime/Viewer/rfc822.php
new file mode 100644 (file)
index 0000000..b8a10be
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_rfc822 class renders out messages from the
+ * message/rfc822 content type.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_rfc822 extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the currently set contents.
+     *
+     * @param array $params  An array with any parameters needed.
+     *
+     * @return string  The rendered text.
+     */
+    public function render($params = array())
+    {
+        if (!$this->mime_part) {
+            return $this->formatStatusMsg(_("There was an error displaying this message part"));
+        }
+
+        $part = &Util::cloneObject($this->mime_part);
+        $part->transferDecodeContents();
+        $text = $part->getContents();
+
+        return $text
+            ? $text
+            : $this->formatStatusMsg(_("There was an error displaying this message part"));
+    }
+
+    /**
+     * Render out attachment information.
+     *
+     * @param array $params  An array with any parameters needed.
+     *
+     * @return string  The rendered text in HTML.
+     */
+    public function renderAttachmentInfo($params = array())
+    {
+        if (!$this->mime_part) {
+            return '';
+        }
+
+        /* 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();
+        if (empty($text)) {
+            return '';
+        }
+
+        /* 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/Headers.php';
+        $headers = Horde_MIME_Headers::parseHeaders($text);
+
+        $header_array = array(
+            'date' => _("Date"),
+            'from' => _("From"),
+            'to' => _("To"),
+            'cc' => _("Cc"),
+            'bcc' => _("Bcc"),
+            'reply-to' => _("Reply-To"),
+            'subject' => _("Subject")
+        );
+        $header_output = array();
+
+        foreach ($header_array as $key => $val) {
+            $hdr = $headers->getValue($key);
+            if (!empty($hdr)) {
+                $header_output[] = '<strong>' . $val . ':</strong> ' . htmlspecialchars($hdr);
+            }
+        }
+
+        require_once 'Horde/Text/Filter.php';
+        return '<div class="mimeHeaders">' . Text_Filter::filter(implode("<br />\n", $header_output), 'emails') . '</div>';
+    }
+
+    /**
+     * Return the MIME content type for the rendered data.
+     *
+     * @return string  The content type of the data.
+     */
+    public function getType()
+    {
+        return 'text/plain; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/richtext.php b/framework/mime_tmp/Horde/Mime/Viewer/richtext.php
new file mode 100644 (file)
index 0000000..e8c6965
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+/**
+ * The Horde_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
+ * 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>,
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_richtext extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the currently set contents in HTML format.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public 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('&lt;', ' '), $text);
+        } else {
+            $text = preg_replace(array('/<lt>/i', "/\r\n/"), array('&lt;', ' '), $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('&lt;nl&gt;', '&lt;np&gt;'), array('<br />', '<p />'), $text);
+        } else {
+            $text = preg_replace(array('/(?<!&lt;)&lt;nl.*&gt;/Uis', '/(?<!&lt;)&lt;np.*&gt;/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. */
+        $pattern = array(
+            '/(?<!&lt;)&lt;bold.*&gt;(.*)&lt;\/bold&gt;/Uis',
+            '/(?<!&lt;)&lt;italic.*&gt;(.*)&lt;\/italic&gt;/Uis',
+            '/(?<!&lt;)&lt;fixed.*&gt;(.*)&lt;\/fixed&gt;/Uis',
+            '/(?<!&lt;)&lt;smaller.*&gt;(.*)&lt;\/smaller&gt;/Uis',
+            '/(?<!&lt;)&lt;bigger.*&gt;(.*)&lt;\/bigger&gt;/Uis',
+            '/(?<!&lt;)&lt;underline.*&gt;(.*)&lt;\/underline&gt;/Uis',
+            '/(?<!&lt;)&lt;center.*&gt;(.*)&lt;\/center&gt;/Uis',
+            '/(?<!&lt;)&lt;flushleft.*&gt;(.*)&lt;\/flushleft&gt;/Uis',
+            '/(?<!&lt;)&lt;flushright.*&gt;(.*)&lt;\/flushright&gt;/Uis',
+            '/(?<!&lt;)&lt;indent.*&gt;(.*)&lt;\/indent&gt;/Uis',
+            '/(?<!&lt;)&lt;excerpt.*&gt;(.*)&lt;\/excerpt&gt;/Uis',
+            '/(?<!&lt;)&lt;subscript.*&gt;(.*)&lt;\/subscript&gt;/Uis',
+            '/(?<!&lt;)&lt;superscript.*&gt;(.*)&lt;\/superscript&gt;/Uis',
+            '/(?<!&lt;)&lt;heading.*&gt;(.*)&lt;\/heading&gt;/Uis',
+            '/(?<!&lt;)&lt;footing.*&gt;(.*)&lt;\/footing&gt;/Uis',
+            '/(?<!&lt;)&lt;paragraph.*&gt;(.*)&lt;\/paragraph&gt;/Uis',
+            '/(?<!&lt;)&lt;signature.*&gt;(.*)&lt;\/signature&gt;/Uis',
+        );
+        $replace = array(
+            '<span style="font-weight: bold">\1</span>',
+            '<span style="font-style: italic">\1</span>',
+            '<font face="fixed">\1</font>',
+            '<span style="font-size: smaller">\1</span>',
+            '<span style="font-size: larger">\1</span>',
+            '<span style="text-decoration: underline">\1</span>',
+            '<div align="center">\1</div>',
+            '<div align="left">\1</div>',
+            '<div align="right">\1</div>',
+            '<blockquote>\1</blockquote>',
+            '<cite>\1</cite>',
+            '<sub>\1</sub>',
+            '<sup>\1</sup>',
+            '<br /><div align="center" style="font-weight: bold">\1</div><br />',
+            '<br /><div align="center" style="font-weight: bold">\1</div><br />',
+            '<p>\1</p>',
+            '<address>\1</address>',
+        );
+        $text = preg_replace($pattern, $replace, $text);
+
+        /* Now we remove the leading/trailing space we added at the start. */
+        $text = substr($text, 1, -1);
+
+        /* Wordwrap. */
+        $text = str_replace(array("\t", '  ', "\n "), array('        ', ' &nbsp;', "\n&nbsp;"), $text);
+        if ($text[0] == ' ') {
+            $text = '&nbsp;' . substr($text, 1);
+        }
+
+        return '<p class="fixed">' . nl2br($text) . '</p>';
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/rpm.php b/framework/mime_tmp/Horde/Mime/Viewer/rpm.php
new file mode 100644 (file)
index 0000000..5c12665
--- /dev/null
@@ -0,0 +1,55 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_rpm class renders out lists of files in RPM
+ * packages by using the rpm tool to query the package.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@recoil.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_rpm extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the RPM contents.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['rpm']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['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.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/rtf.php b/framework/mime_tmp/Horde/Mime/Viewer/rtf.php
new file mode 100644 (file)
index 0000000..e48e634
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_rtf class renders out Rich Text Format documents in
+ * HTML format by using the UnRTF package
+ * (http://www.gnu.org/software/unrtf/unrtf.html).
+ *
+ * Copyright 2007 Duck <duck@obala.net>
+ *
+ * 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  Duck <duck@obala.net>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_rtf extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current data using UnRTF.
+     *
+     * @param array $params  Any parameters the viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['rtf']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['rtf']['location']) . '</pre>';
+        }
+
+        $tmp_rtf = Horde::getTempFile('rtf');
+        $tmp_output = Horde::getTempFile('rtf');
+        $args = " $tmp_rtf > $tmp_output";
+
+        $fh = fopen($tmp_rtf, 'w');
+        fwrite($fh, $this->mime_part->getContents());
+        fclose($fh);
+
+        exec($GLOBALS['mime_drivers']['horde']['rtf']['location'] . $args);
+
+        if (!file_exists($tmp_output)) {
+            return _("Unable to translate this RTF document");
+        }
+
+        return file_get_contents($tmp_output);
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/security.php b/framework/mime_tmp/Horde/Mime/Viewer/security.php
new file mode 100644 (file)
index 0000000..480b6e4
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_security class is a wrapper used to load the
+ * appropriate Horde_MIME_Viewer for secure multipart messages (defined by RFC
+ * 1847). This class handles multipart/signed and multipart/encrypted data.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_security extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Stores the Horde_MIME_Viewer of the specified security protocol.
+     *
+     * @var Horde_MIME_Viewer
+     */
+    protected $_viewer;
+
+    /**
+     * The $mime_part class variable has the information to render
+     * out, encapsulated in a Horde_MIME_Part object.
+     *
+     * @param $params mixed  The parameters (if any) to pass to the underlying
+     *                       Horde_MIME_Viewer.
+     *
+     * @return string  Rendering of the content.
+     */
+    public function render($params = array())
+    {
+        /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */
+        if (!($this->_resolveViewer())) {
+            return;
+        }
+
+        /* Render using the loaded Horde_MIME_Viewer object. */
+        return $this->_viewer->render($params);
+    }
+
+    /**
+     * Returns the content-type of the Viewer used to view the part.
+     *
+     * @return string  A content-type string.
+     */
+    public function getType()
+    {
+        /* Get the appropriate Horde_MIME_Viewer for the protocol specified. */
+        if (!($this->_resolveViewer())) {
+            return 'application/octet-stream';
+        } else {
+            return $this->_viewer->getType();
+        }
+    }
+
+    /**
+     * Load a Horde_MIME_Viewer according to the protocol parameter stored
+     * in the Horde_MIME_Part to render. If unsuccessful, try to load a generic
+     * multipart Horde_MIME_Viewer.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    protected function _resolveViewer()
+    {
+        $viewer = null;
+
+        if (empty($this->_viewer)) {
+            $protocol = $this->mime_part->getContentTypeParameter('protocol');
+            if (empty($protocol)) {
+                return false;
+            }
+            $viewer = &Horde_MIME_Viewer::factory($this->mime_part, $protocol);
+            if (empty($viewer) ||
+                (String::lower(get_class($viewer)) == 'mime_viewer_default')) {
+                $viewer = &Horde_MIME_Viewer::factory($this->mime_part, $this->mime_part->getPrimaryType() . '/*');
+                if (empty($viewer)) {
+                    return false;
+                }
+            }
+            $this->_viewer = $viewer;
+        }
+
+        return true;
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/simple.php b/framework/mime_tmp/Horde/Mime/Viewer/simple.php
new file mode 100644 (file)
index 0000000..d2eecd3
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_simple class renders out plain text without any
+ * modifications.
+ *
+ * Copyright 2004-2008 The Horde Project (http://www.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  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_simple extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Return the MIME type of the rendered content.
+     *
+     * @return string  MIME-type of the output content.
+     */
+    public function getType()
+    {
+        return 'text/plain';
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/smil.php b/framework/mime_tmp/Horde/Mime/Viewer/smil.php
new file mode 100644 (file)
index 0000000..8f521a4
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_smil renders SMIL documents to very basic HTML.
+ *
+ * Copyright 2006-2008 The Horde Project (http://www.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@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_smil extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Handle for the XML parser object.
+     *
+     * @var resource
+     */
+    protected $_parser;
+
+    /**
+     * String buffer to hold the generated content
+     *
+     * @var string
+     */
+    protected $_content = '';
+
+    /**
+     * Renders out the contents.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Create a new parser and set its default properties. */
+        $this->_parser = xml_parser_create();
+        xml_set_object($this->_parser, $this);
+        xml_set_element_handler($this->_parser, '_startElement', '_endElement');
+        xml_set_character_data_handler($this->_parser, '_defaultHandler');
+        xml_parse($this->_parser, $this->mime_part->getContents(), true);
+        return $this->_content;
+    }
+
+    /**
+     * User-defined function callback for start elements.
+     *
+     * @param object $parser  Handle to the parser instance.
+     * @param string $name    The name of this XML element.
+     * @param array $attrs    List of this element's attributes.
+     */
+    protected function _startElement($parser, $name, $attrs)
+    {
+        switch ($name) {
+        case 'IMG':
+            if (isset($attrs['SRC'])) {
+                $this->_content .= '<img src="' . htmlspecialchars($attrs['SRC']) . '" />';
+            }
+            break;
+        }
+    }
+
+    /**
+     * User-defined function callback for end elements.
+     *
+     * @param object $parser  Handle to the parser instance.
+     * @param string $name    The name of this XML element.
+     */
+    protected function _endElement($parser, $name)
+    {
+    }
+
+    /**
+     * User-defined function callback for character data.
+     *
+     * @param object $parser  Handle to the parser instance.
+     * @param string $data    String of character data.
+     */
+    protected function _defaultHandler($parser, $data)
+    {
+        $data = trim($data);
+        if (!empty($data)) {
+            $this->_content .= ' ' . htmlspecialchars($data);
+        }
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/source.php b/framework/mime_tmp/Horde/Mime/Viewer/source.php
new file mode 100644 (file)
index 0000000..336946b
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_source class is a class for any viewer that wants
+ * to provide line numbers to extend.
+ *
+ * Copyright 1999-2008 The Horde Project (http://www.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@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_source extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Add line numbers to a block of code.
+     *
+     * @param string $code  The code to number.
+     */
+    public function lineNumber($code, $linebreak = "\n")
+    {
+        $lines = substr_count($code, $linebreak) + 1;
+        $html = '<table class="lineNumbered" cellspacing="0"><tr><th>';
+        for ($l = 1; $l <= $lines; $l++) {
+            $html .= sprintf('<a id="l%s" href="#l%s">%s</a><br />', $l, $l, $l) . "\n";
+        }
+        return $html . '</th><td><div>' . $code . '</div></td></tr></table>';
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/srchighlite.php b/framework/mime_tmp/Horde/Mime/Viewer/srchighlite.php
new file mode 100644 (file)
index 0000000..aa04725
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+
+require_once dirname(__FILE__) . '/source.php';
+
+/**
+ * The Horde_MIME_Viewer_srchighlite class renders out various content in HTML
+ * format by using Source-highlight.
+ *
+ * Source-highlight: http://www.gnu.org/software/src-highlite/
+ *
+ * Copyright 2003-2008 The Horde Project (http://www.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  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_srchighlite extends Horde_MIME_Viewer_source
+{
+    /**
+     * Render out the currently set contents using Source-highlight
+     *
+     * @param array $params  Any parameters the viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['srchighlite']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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;
+        }
+
+        /* Execute Source-Highlite. */
+        exec($GLOBALS['mime_drivers']['horde']['srchighlite']['location'] . " --src-lang $lang --out-format xhtml --input $tmpin --output $tmpout");
+        $results = file_get_contents($tmpout);
+        unlink($tmpout);
+
+        /* Educated Guess at whether we are inline or not. */
+        if (headers_sent() || ob_get_length()) {
+            return $this->lineNumber($results);
+        } else {
+            return Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-header.inc') .
+                $this->lineNumber($results) .
+                Util::bufferOutput('require', $GLOBALS['registry']->get('templates', 'horde') . '/common-footer.inc');
+        }
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/tgz.php b/framework/mime_tmp/Horde/Mime/Viewer/tgz.php
new file mode 100644 (file)
index 0000000..4bd7664
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+require_once 'Horde/Compress.php';
+require_once 'Horde/Text.php';
+
+/**
+ * The Horde_MIME_Viewer_tgz class renders out plain or gzipped tarballs in
+ * HTML.
+ *
+ * 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@recoil.org>
+ * @author  Michael Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_tgz extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the currently set tar file contents.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        $contents = $this->mime_part->getContents();
+
+        /* Only decompress gzipped files. */
+        $subtype = $this->mime_part->getSubType();
+        if (($subtype == 'x-compressed-tar') ||
+            ($subtype == 'tgz') ||
+            ($subtype == 'x-tgz') ||
+            ($subtype == 'gzip') ||
+            ($subtype == 'x-gzip') ||
+            ($subtype == 'x-gzip-compressed') ||
+            ($subtype == 'x-gtar')) {
+            $gzip = &Horde_Compress::singleton('gzip');
+            $contents = $gzip->decompress($contents);
+            if (empty($contents)) {
+                return _("Unable to open compressed archive.");
+            } elseif (is_a($contents, 'PEAR_Error')) {
+                return $contents->getMessage();
+            }
+        }
+
+        if ($subtype == 'gzip' ||
+            $subtype == 'x-gzip' ||
+            $subtype == 'x-gzip-compressed') {
+            global $conf;
+            require_once 'Horde/MIME/Magic.php';
+            $mime_type = MIME_Magic::analyzeData($contents, isset($conf['mime']['magic_db']) ? $conf['mime']['magic_db'] : null);
+            if (!$mime_type) {
+                $mime_type = _("Unknown");
+            }
+            return sprintf(_("Content type of compressed file: %s"), $mime_type);
+        }
+
+        /* Obtain the list of files/data in the tar file. */
+        $tar = Horde_Compress::factory('tar');
+        $tarData = $tar->decompress($contents);
+        if (is_a($tarData, 'PEAR_Error')) {
+            return $tarData->getMessage();
+        }
+
+        $fileCount = count($tarData);
+        $text = '<strong>' . htmlspecialchars(sprintf(_("Contents of \"%s\""), $this->mime_part->getName())) . ':</strong>' . "\n" .
+            '<table><tr><td align="left"><tt><span class="fixed">' .
+            Text::htmlAllSpaces(_("Archive Name") . ':  ' . $this->mime_part->getName()) . "\n" .
+            Text::htmlAllSpaces(_("Archive File Size") . ': ' . strlen($contents) . ' bytes') . "\n" .
+            Text::htmlAllSpaces(sprintf(ngettext("File Count: %d file", "File Count: %d files", $fileCount), $fileCount)) .
+            "\n\n" .
+            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" .
+            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";
+        }
+
+        return nl2br($text . str_repeat('-', 106) . "\n" .
+                     '</span></tt></td></tr></table>');
+    }
+
+    /**
+     * Return the content-type
+     *
+     * @return string  The content-type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/tnef.php b/framework/mime_tmp/Horde/Mime/Viewer/tnef.php
new file mode 100644 (file)
index 0000000..64856df
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_tnef class allows MS-TNEF attachments to be
+ * displayed.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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@horde.org>
+ * @author  Michael Slusarz <slusarz@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_tnef extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current tnef data.
+     *
+     * @param array $params  Any parameters the viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public 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.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/vcard.php b/framework/mime_tmp/Horde/Mime/Viewer/vcard.php
new file mode 100644 (file)
index 0000000..b116eb8
--- /dev/null
@@ -0,0 +1,372 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_vcard class renders out vCards in HTML format.
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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@horde.org>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_vcard extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the vcard contents.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = null)
+    {
+        global $registry, $prefs, $notification;
+
+        require_once 'Horde/iCalendar.php';
+
+        $app = false;
+        $data = $this->mime_part->getContents();
+        $html = '';
+        $import_msg = null;
+        $title = _("vCard");
+
+        $iCal = new Horde_iCalendar();
+        if (!$iCal->parsevCalendar($data, 'VCALENDAR',
+                                   $this->mime_part->getCharset())) {
+            $notification->push(
+                _("There was an error reading the contact data."),
+                'horde.error');
+        }
+
+        if (Util::getFormData('import') &&
+            Util::getFormData('source') &&
+            $registry->hasMethod('contacts/import')) {
+            $source = Util::getFormData('source');
+            $contacts = $registry->call('contacts/import',
+                                        array($data, 'text/x-vcard', $source));
+            if (is_a($contacts, 'PEAR_Error')) {
+                $notification->push(
+                    _("There was an error importing the contact data:") . ' '
+                    . $contacts->getMessage(),
+                    'horde.error');
+            } else {
+                $notification->push(sprintf(ngettext(
+                    "%d contact was successfully added to your address book.",
+                    "%d contacts were successfully added to your address book.",
+                    $iCal->getComponentCount()),
+                                            $iCal->getComponentCount()),
+                                    'horde.success');
+            }
+        }
+
+        $html .= '<table cellspacing="1" border="0" cellpadding="1">';
+
+        $i = 0;
+        foreach ($iCal->getComponents() as $vc) {
+            if ($i > 0) {
+                $html .= '<tr><td colspan="2">&nbsp;</td></tr>';
+            }
+            ++$i;
+
+            $html .= '<tr><td colspan="2" class="header">';
+            $fullname = $vc->getAttributeDefault('FN', false);
+            if ($fullname !== false) {
+                $html .= $fullname;
+            }
+            $html .= '</td></tr>';
+
+            $n = $vc->printableName();
+            if (!empty($n)) {
+                $html .= $this->_row(_("Name"), $n);
+            }
+
+            $aliases = $vc->getAttributeValues('ALIAS');
+            if (!is_a($aliases, 'PEAR_Error')) {
+                $html .= $this->_row(_("Alias"), implode("\n", $aliases));
+            }
+            $birthdays = $vc->getAttributeValues('BDAY');
+            if (!is_a($birthdays, 'PEAR_Error')) {
+                $birthday = new Horde_Date($birthdays[0]);
+                $html .= $this->_row(
+                    _("Birthday"),
+                    $birthday->strftime($prefs->getValue('date_format')));
+            }
+
+            $photos = $vc->getAllAttributes('PHOTO');
+            foreach ($photos as $photo) {
+                if (!isset($photo['params']['VALUE']) ||
+                    String::upper($photo['params']['VALUE']) != 'URI') {
+                    continue;
+                }
+                $html .= $this->_row(_("Photo"),
+                                     '<img src="' . htmlspecialchars($photo['value']) . '" />',
+                                     false);
+            }
+
+            $labels = $vc->getAllAttributes('LABEL');
+            foreach ($labels as $label) {
+                if (isset($label['params']['TYPE'])) {
+                    if (!is_array($item['params']['TYPE'])) {
+                        $item['params']['TYPE'] = array($item['params']['TYPE']);
+                    }
+                } else {
+                    $item['params']['TYPE'] = array_keys($item['params']);
+                }
+                $types = array();
+                foreach ($item['params']['TYPE'] as $type) {
+                    switch(String::upper($type)) {
+                    case 'HOME':
+                        $types[] = _("Home Address");
+                        break;
+                    case 'WORK':
+                        $types[] = _("Work Address");
+                        break;
+                    case 'DOM':
+                        $types[] = _("Domestic Address");
+                        break;
+                    case 'INTL':
+                        $types[] = _("International Address");
+                        break;
+                    case 'POSTAL':
+                        $types[] = _("Postal Address");
+                        break;
+                    case 'PARCEL':
+                        $types[] = _("Parcel Address");
+                        break;
+                    case 'PREF':
+                        $types[] = _("Preferred Address");
+                        break;
+                    default:
+                        $types[] = _("Address");
+                        break;
+                    }
+                }
+                $html .= $this->_row(implode('/', $types), $label['value']);
+            }
+
+            $adrs = $vc->getAllAttributes('ADR');
+            foreach ($adrs as $item) {
+                if (isset($item['params']['TYPE'])) {
+                    if (!is_array($item['params']['TYPE'])) {
+                        $item['params']['TYPE'] = array($item['params']['TYPE']);
+                    }
+                } else {
+                    $item['params']['TYPE'] = array_keys($item['params']);
+                }
+                $address = $item['values'];
+                $a = array();
+                if (isset($address[VCARD_ADR_STREET])) {
+                    $a[] = $address[VCARD_ADR_STREET];
+                }
+                if (isset($address[VCARD_ADR_LOCALITY])) {
+                    $a[] = $address[VCARD_ADR_LOCALITY];
+                }
+                if (isset($address[VCARD_ADR_REGION])) {
+                    $a[] = $address[VCARD_ADR_REGION];
+                }
+                if (isset($address[VCARD_ADR_POSTCODE])) {
+                    $a[] = $address[VCARD_ADR_POSTCODE];
+                }
+                if (isset($address[VCARD_ADR_COUNTRY])) {
+                    $a[] = $address[VCARD_ADR_COUNTRY];
+                }
+                $types = array();
+                foreach ($item['params']['TYPE'] as $type) {
+                    switch(String::upper($type)) {
+                    case 'HOME':
+                        $types[] = _("Home Address");
+                        break;
+                    case 'WORK':
+                        $types[] = _("Work Address");
+                        break;
+                    case 'DOM':
+                        $types[] = _("Domestic Address");
+                        break;
+                    case 'INTL':
+                        $types[] = _("International Address");
+                        break;
+                    case 'POSTAL':
+                        $types[] = _("Postal Address");
+                        break;
+                    case 'PARCEL':
+                        $types[] = _("Parcel Address");
+                        break;
+                    case 'PREF':
+                        $types[] = _("Preferred Address");
+                        break;
+                    default:
+                        $types[] = _("Address");
+                        break;
+                    }
+                }
+                $html .= $this->_row(implode('/', $types), implode("\n", $a));
+            }
+
+            $numbers = $vc->getAllAttributes('TEL');
+
+            foreach ($numbers as $number) {
+                if (isset($number['params']['TYPE'])) {
+                    if (!is_array($number['params']['TYPE'])) {
+                        $number['params']['TYPE'] = array($number['params']['TYPE']);
+                    }
+                    foreach ($number['params']['TYPE'] as $type) {
+                        $number['params'][String::upper($type)] = true;
+                    }
+                }
+                if (isset($number['params']['FAX'])) {
+                    $html .= $this->_row(_("Fax"), $number['value']);
+                } else {
+                    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']);
+                    }
+                }
+            }
+
+            $addresses = $vc->getAllAttributes('EMAIL');
+            $emails = array();
+            foreach ($addresses as $address) {
+                if (isset($address['params']['TYPE'])) {
+                    if (!is_array($address['params']['TYPE'])) {
+                        $address['params']['TYPE'] = array($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:' . htmlspecialchars($address['value']);
+                }
+                $email .= '">' . htmlspecialchars($address['value']) . '</a>';
+                if (isset($address['params']['PREF'])) {
+                    array_unshift($emails, $email);
+                } else {
+                    $emails[] = $email;
+                }
+            }
+
+            if (count($emails)) {
+                $html .= $this->_row(_("Email"), implode("\n", $emails), false);
+            }
+
+            $title = $vc->getAttributeValues('TITLE');
+            if (!is_a($title, 'PEAR_Error')) {
+                $html .= $this->_row(_("Title"), $title[0]);
+            }
+
+            $role = $vc->getAttributeValues('ROLE');
+            if (!is_a($role, 'PEAR_Error')) {
+                $html .= $this->_row(_("Role"), $role[0]);
+            }
+
+            $org = $vc->getAttributeValues('ORG');
+            if (!is_a($org, 'PEAR_Error')) {
+                $html .= $this->_row(_("Company"), $org[0]);
+                if (isset($org[1])) {
+                    $html .= $this->_row(_("Department"), $org[1]);
+                }
+            }
+
+            $notes = $vc->getAttributeValues('NOTE');
+            if (!is_a($notes, 'PEAR_Error')) {
+                $html .= $this->_row(_("Notes"), $notes[0]);
+            }
+
+            $url = $vc->getAttributeValues('URL');
+            if (!is_a($url, 'PEAR_Error')) {
+                $html .= $this->_row(
+                    _("URL"),
+                    '<a href="' . htmlspecialchars($url[0])
+                        . '" target="_blank">' . htmlspecialchars($url[0])
+                        . '</a>',
+                    false);
+            }
+        }
+
+        if ($registry->hasMethod('contacts/import') &&
+            $registry->hasMethod('contacts/sources')) {
+            $html .= '<tr><td colspan="2" class="smallheader"><form action="'
+                . Horde::selfUrl() . '" method="get" name="vcard_import">'
+                . Util::formInput();
+            foreach ($_GET as $key => $val) {
+                $html .= '<input type="hidden" name="' . htmlspecialchars($key)
+                    . '" value="' . htmlspecialchars($val) . '" />';
+            }
+
+            $sources = $registry->call('contacts/sources', array(true));
+            if (count($sources) > 1) {
+                $html .=
+                    '<input type="submit" class="button" name="import" value="'
+                    . _("Add to address book:") . '" />'
+                    . '<label for="add_source" class="hidden">'
+                    . _("Address Book") . '</label>'
+                    . '<select id="add_source" name="source">';
+                foreach ($sources as $key => $label) {
+                    $selected = ($key == $prefs->getValue('add_source'))
+                        ? ' selected="selected"' : '';
+                    $html .= '<option value="' . htmlspecialchars($key) . '"'
+                        . $selected . '>' . htmlspecialchars($label)
+                        . '</option>';
+                }
+            } else {
+                reset($sources);
+                $html .=
+                    '<input type="submit" class="button" name="import" value="'
+                    . _("Add to my address book") . '" />'
+                    . '<input type="hidden" name="source" value="'
+                    . htmlspecialchars(key($sources)) . '" />';
+            }
+
+            $html .= '</form></td></tr><tr><td>&nbsp;</td></tr>';
+        }
+
+        $html .=  '</table>';
+
+        return
+            Util::bufferOutput(
+                'include',
+                $registry->get('templates', 'horde') . '/common-header.inc')
+            . Util::bufferOutput(array($notification, 'notify'),
+                                 array('listeners' => 'status'))
+            . $html
+            . Util::bufferOutput(
+                'include',
+                $registry->get('templates', 'horde') . '/common-footer.inc');
+    }
+
+    function _row($label, $value, $encode = true)
+    {
+        if ($encode) {
+            $label = htmlspecialchars($label);
+            $value = htmlspecialchars($value);
+        }
+        return '<tr><td class="item" valign="top">' . $label
+            . '</td><td class="item" valign="top">' . nl2br($value)
+            . "</td></tr>\n";
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/webcpp.php b/framework/mime_tmp/Horde/Mime/Viewer/webcpp.php
new file mode 100644 (file)
index 0000000..6c4ad0d
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * The Horde_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/
+ *
+ * Copyright 2002-2008 The Horde Project (http://www.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  Mike Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_webcpp extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the currently set contents using Web C Plus Plus.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    protected function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['webcpp']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['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($GLOBALS['mime_drivers']['horde']['webcpp']['location'] . " --pipe --pipe -x=$ext -l -a -t < $tmpin > $tmpout");
+        $results = file_get_contents($tmpout);
+
+        /* If we are not displaying inline, all the formatting is already
+         * done for us. */
+        if (!$this->viewInline()) {
+            /* The first 2 lines are the Content-Type line and a blank line
+             * so we should remove them before outputting. */
+            return preg_replace("/.*\n.*\n/", '', $results, 1);
+        }
+
+        /* Extract the style sheet, removing any global body formatting
+         * if we're displaying inline. */
+        $res = preg_split(';(</style>)|(<style type="text/css">);', $results);
+        $style = $res[1];
+        $style = preg_replace('/\nbody\s+?{.*?}/s', '', $style);
+
+        /* Extract the content. */
+        $res = preg_split('/\<\/?pre\>/', $results);
+        $body = $res[1];
+
+        return '<style>' . $style . '</style><div class="webcpp" style="white-space:pre;font-family:Lucida Console,Courier,monospace;">' . $body . '</div>';
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    protected function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/wordperfect.php b/framework/mime_tmp/Horde/Mime/Viewer/wordperfect.php
new file mode 100644 (file)
index 0000000..c65184c
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_wordperfect class renders out WordPerfect documents
+ * in HTML format by using the libwpd package.
+ *
+ * libpwd website: http://libwpd.sourceforge.net/
+ *
+ * Copyright 2007-2008 The Horde Project (http://www.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  Matt Selsky <selsky@columbia.edu>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_wordperfect extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current data using wpd2html.
+     *
+     * @param array $params  Any parameters the viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        /* Check to make sure the program actually exists. */
+        if (!file_exists($GLOBALS['mime_drivers']['horde']['wordperfect']['location'])) {
+            return '<pre>' . sprintf(_("The program used to view this data type (%s) was not found on the system."), $GLOBALS['mime_drivers']['horde']['wordperfect']['location']) . '</pre>';
+        }
+
+        $tmp_wpd = Horde::getTempFile('wpd');
+        $tmp_output = Horde::getTempFile('wpd');
+        $args = " $tmp_wpd > $tmp_output";
+
+        $fh = fopen($tmp_wpd, 'w');
+        fwrite($fh, $this->mime_part->getContents());
+        fclose($fh);
+
+        exec($GLOBALS['mime_drivers']['horde']['wordperfect']['location'] . $args);
+
+        if (!file_exists($tmp_output)) {
+            return _("Unable to translate this WordPerfect document");
+        }
+
+        return file_get_contents($tmp_output);
+    }
+
+    /**
+     * Return the MIME content type of the rendered content.
+     *
+     * @return string  The content type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/Viewer/zip.php b/framework/mime_tmp/Horde/Mime/Viewer/zip.php
new file mode 100644 (file)
index 0000000..ebdca0b
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+/**
+ * The Horde_MIME_Viewer_zip class renders out the contents of ZIP files in
+ * HTML format.
+ *
+ * Copyright 2000-2008 The Horde Project (http://www.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@horde.org>
+ * @author  Michael Cochrane <mike@graftonhall.co.nz>
+ * @package Horde_MIME_Viewer
+ */
+class Horde_MIME_Viewer_zip extends Horde_MIME_Viewer_Driver
+{
+    /**
+     * Render out the current zip contents.
+     *
+     * @param array $params  Any parameters the Viewer may need.
+     *
+     * @return string  The rendered contents.
+     */
+    public function render($params = array())
+    {
+        return $this->_render($this->mime_part->getContents());
+    }
+
+    /**
+     * Output the file list.
+     *
+     * @param string $contents  The contents of the zip archive.
+     * @param mixed $callback   The callback function to use on the zipfile
+     *                          information.
+     *
+     * @return string  The file list.
+     */
+    protected function _render($contents, $callback = null)
+    {
+        require_once 'Horde/Compress.php';
+
+        $zip = &Horde_Compress::factory('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));
+        if (is_a($zipInfo, 'PEAR_Error')) {
+            return $zipInfo->getMessage();
+        }
+        $fileCount = count($zipInfo);
+
+        /* Determine maximum file name length. */
+        $maxlen = 0;
+        foreach ($zipInfo as $val) {
+            $maxlen = max($maxlen, strlen($val['name']));
+        }
+
+        require_once 'Horde/Text.php';
+
+        $text = '<strong>'
+            . htmlspecialchars(sprintf(_("Contents of \"%s\""),
+                                       $this->mime_part->getName()))
+            . ':</strong>' . "\n"
+            . '<table><tr><td align="left"><tt><span class="fixed">'
+            . Text::htmlAllSpaces(
+                _("Archive Name") . ': ' . $this->mime_part->getName() . "\n"
+                . _("Archive File Size") . ': ' . strlen($contents)
+                . ' bytes' . "\n"
+                . sprintf(
+                    ngettext("File Count: %d file", "File Count: %d files",
+                             $fileCount),
+                    $fileCount)
+                . "\n\n"
+                . String::pad(_("File Name"),     $maxlen, ' ', STR_PAD_RIGHT)
+                . String::pad(_("Attributes"),    10,      ' ', STR_PAD_LEFT)
+                . String::pad(_("Size"),          10,      ' ', STR_PAD_LEFT)
+                . String::pad(_("Modified Date"), 19,      ' ', STR_PAD_LEFT)
+                . String::pad(_("Method"),        10,      ' ', STR_PAD_LEFT)
+                . String::pad(_("CRC"),           10,      ' ', STR_PAD_LEFT)
+                . String::pad(_("Ratio"),         10,      ' ', STR_PAD_LEFT)
+                . "\n")
+            . str_repeat('-', 69 + $maxlen) . "\n";
+
+        foreach ($zipInfo as $key => $val) {
+            $ratio = (empty($val['size']))
+                ? 0
+                : 100 * ($val['csize'] / $val['size']);
+
+            $val['name']   = String::pad($val['name'],
+                                         $maxlen, ' ', STR_PAD_RIGHT);
+            $val['attr']   = String::pad($val['attr'],
+                                         10,      ' ', STR_PAD_LEFT);
+            $val['size']   = String::pad($val['size'],
+                                         10,      ' ', STR_PAD_LEFT);
+            $val['date']   = String::pad(strftime("%d-%b-%Y %H:%M",
+                                                  $val['date']),
+                                         19,      ' ', STR_PAD_LEFT);
+            $val['method'] = String::pad($val['method'],
+                                         10,      ' ', STR_PAD_LEFT);
+            $val['crc']    = String::pad($val['crc'],
+                                         10,      ' ', STR_PAD_LEFT);
+            $val['ratio']  = String::pad(sprintf("%1.1f%%", $ratio),
+                                         10,      ' ', 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('-', 69 + $maxlen) . "\n"
+            . '</span></tt></td></tr></table>';
+
+        return nl2br($text);
+    }
+
+    /**
+     * Return the content-type
+     *
+     * @return string  The content-type of the output.
+     */
+    public function getType()
+    {
+        return 'text/html; charset=' . NLS::getCharset();
+    }
+}
diff --git a/framework/mime_tmp/Horde/Mime/mime.magic.php b/framework/mime_tmp/Horde/Mime/mime.magic.php
new file mode 100644 (file)
index 0000000..d63efbe
Binary files /dev/null and b/framework/mime_tmp/Horde/Mime/mime.magic.php differ
diff --git a/framework/mime_tmp/Horde/Mime/mime.mapping.php b/framework/mime_tmp/Horde/Mime/mime.mapping.php
new file mode 100644 (file)
index 0000000..82aebac
--- /dev/null
@@ -0,0 +1,894 @@
+<?php
+/**
+ * This file contains a mapping of common file extensions to
+ * MIME types. It has been automatically generated from the
+ * framework/devtools/mime_mapping directory.
+ *
+ * ALL changes should be made to framework/devtools/mime_mapping/mime.types.horde
+ * or else they will be lost when this file is regenerated.
+ *
+ * Any unknown file extensions will automatically be mapped to
+ * 'x-extension/<ext>' where <ext> is the unknown file extension.
+ *
+ * @package Horde_MIME
+ *
+ * $Horde: framework/MIME/MIME/mime.mapping.php,v 1.18 2008/11/06 04:33:47 chuck Exp $
+ *
+ * Generated: 11/05/08 23:30:23 by chuck on technest.org
+ */
+$mime_extension_map = array(
+    '__MAXPERIOD__' => '1',
+    'ez'            => 'application/andrew-inset',
+    'atom'          => 'application/atom+xml',
+    'atomcat'       => 'application/atomcat+xml',
+    'atomsvc'       => 'application/atomsvc+xml',
+    'ccxml'         => 'application/ccxml+xml',
+    'davmount'      => 'application/davmount+xml',
+    'ecma'          => 'application/ecmascript',
+    'pfr'           => 'application/font-tdpfr',
+    'stk'           => 'application/hyperstudio',
+    'js'            => 'application/x-javascript',
+    'json'          => 'application/json',
+    'hqx'           => 'application/mac-binhex40',
+    'cpt'           => 'application/mac-compactpro',
+    'mrc'           => 'application/marc',
+    'ma'            => 'application/mathematica',
+    'nb'            => 'application/mathematica',
+    'mb'            => 'application/mathematica',
+    'mathml'        => 'application/mathml+xml',
+    'mbox'          => 'application/mbox',
+    'mscml'         => 'application/mediaservercontrol+xml',
+    'mp4s'          => 'application/mp4',
+    'doc'           => 'application/msword',
+    'dot'           => 'application/msword',
+    'mxf'           => 'application/mxf',
+    'bin'           => 'application/octet-stream',
+    'dms'           => 'application/octet-stream',
+    'lha'           => 'application/x-lha',
+    'lzh'           => 'application/x-lha',
+    'class'         => 'application/x-java',
+    'so'            => 'application/x-sharedlib',
+    'iso'           => 'application/x-cd-image',
+    'dmg'           => 'application/octet-stream',
+    'dist'          => 'application/octet-stream',
+    'distz'         => 'application/octet-stream',
+    'pkg'           => 'application/octet-stream',
+    'bpk'           => 'application/octet-stream',
+    'dump'          => 'application/octet-stream',
+    'elc'           => 'application/octet-stream',
+    'oda'           => 'application/oda',
+    'ogg'           => 'application/ogg',
+    'pdf'           => 'application/pdf',
+    'pgp'           => 'application/pgp',
+    'asc'           => 'text/plain',
+    'sig'           => 'application/pgp-signature',
+    'prf'           => 'application/pics-rules',
+    'p10'           => 'application/pkcs10',
+    'p7m'           => 'application/pkcs7-mime',
+    'p7c'           => 'application/pkcs7-mime',
+    'p7s'           => 'application/pkcs7-signature',
+    'cer'           => 'application/x-x509-ca-cert',
+    'crl'           => 'application/pkix-crl',
+    'pkipath'       => 'application/pkix-pkipath',
+    'pki'           => 'application/pkixcmp',
+    'pls'           => 'audio/x-scpls',
+    'ai'            => 'application/illustrator',
+    'eps'           => 'image/x-eps',
+    'ps'            => 'application/postscript',
+    'cww'           => 'application/prs.cww',
+    'rdf'           => 'text/rdf',
+    'rif'           => 'application/reginfo+xml',
+    'rnc'           => 'application/relax-ng-compact-syntax',
+    'rl'            => 'application/resource-lists+xml',
+    'rs'            => 'application/rls-services+xml',
+    'rsd'           => 'application/rsd+xml',
+    'rss'           => 'text/rss',
+    'rtf'           => 'application/rtf',
+    'sbml'          => 'application/sbml+xml',
+    'scq'           => 'application/scvp-cv-request',
+    'scs'           => 'application/scvp-cv-response',
+    'spq'           => 'application/scvp-vp-request',
+    'spp'           => 'application/scvp-vp-response',
+    'sdp'           => 'application/vnd.stardivision.impress',
+    'setpay'        => 'application/set-payment-initiation',
+    'setreg'        => 'application/set-registration-initiation',
+    'shf'           => 'application/shf+xml',
+    'smi'           => 'application/smil',
+    'smil'          => 'application/smil',
+    'rq'            => 'application/sparql-query',
+    'srx'           => 'application/sparql-results+xml',
+    'gram'          => 'application/srgs',
+    'grxml'         => 'application/srgs+xml',
+    'ssml'          => 'application/ssml+xml',
+    'plb'           => 'application/vnd.3gpp.pic-bw-large',
+    'psb'           => 'application/vnd.3gpp.pic-bw-small',
+    'pvb'           => 'application/vnd.3gpp.pic-bw-var',
+    'tcap'          => 'application/vnd.3gpp2.tcap',
+    'pwn'           => 'application/vnd.3m.post-it-notes',
+    'aso'           => 'application/vnd.accpac.simply.aso',
+    'imp'           => 'application/vnd.accpac.simply.imp',
+    'acu'           => 'application/vnd.acucobol',
+    'atc'           => 'application/vnd.acucorp',
+    'acutc'         => 'application/vnd.acucorp',
+    'xdp'           => 'application/vnd.adobe.xdp+xml',
+    'xfdf'          => 'application/vnd.adobe.xfdf',
+    'ami'           => 'application/vnd.amiga.ami',
+    'cii'           => 'application/vnd.anser-web-certificate-issue-initiation',
+    'fti'           => 'application/vnd.anser-web-funds-transfer-initiation',
+    'atx'           => 'application/vnd.antix.game-component',
+    'mpkg'          => 'application/vnd.apple.installer+xml',
+    'aep'           => 'application/vnd.audiograph',
+    'mpm'           => 'application/vnd.blueice.multipass',
+    'bmi'           => 'application/vnd.bmi',
+    'rep'           => 'application/vnd.businessobjects',
+    'cdxml'         => 'application/vnd.chemdraw+xml',
+    'mmd'           => 'application/vnd.chipnuts.karaoke-mmd',
+    'cdy'           => 'application/vnd.cinderella',
+    'cla'           => 'application/vnd.claymore',
+    'c4g'           => 'application/vnd.clonk.c4group',
+    'c4d'           => 'application/vnd.clonk.c4group',
+    'c4f'           => 'application/vnd.clonk.c4group',
+    'c4p'           => 'application/vnd.clonk.c4group',
+    'c4u'           => 'application/vnd.clonk.c4group',
+    'csp'           => 'application/vnd.commonspace',
+    'cst'           => 'application/vnd.commonspace',
+    'cdbcmsg'       => 'application/vnd.contact.cmsg',
+    'cmc'           => 'application/vnd.cosmocaller',
+    'clkx'          => 'application/vnd.crick.clicker',
+    'clkk'          => 'application/vnd.crick.clicker.keyboard',
+    'clkp'          => 'application/vnd.crick.clicker.palette',
+    'clkt'          => 'application/vnd.crick.clicker.template',
+    'clkw'          => 'application/vnd.crick.clicker.wordbank',
+    'wbs'           => 'application/vnd.criticaltools.wbs+xml',
+    'pml'           => 'application/vnd.ctc-posml',
+    'ppd'           => 'application/vnd.cups-ppd',
+    'curl'          => 'application/vnd.curl',
+    'rdz'           => 'application/vnd.data-vision.rdz',
+    'fe_launch'     => 'application/vnd.denovo.fcselayout-link',
+    'dna'           => 'application/vnd.dna',
+    'mlp'           => 'application/vnd.dolby.mlp',
+    'dpg'           => 'application/vnd.dpgraph',
+    'dfac'          => 'application/vnd.dreamfactory',
+    'mag'           => 'application/vnd.ecowin.chart',
+    'nml'           => 'application/vnd.enliven',
+    'esf'           => 'application/vnd.epson.esf',
+    'msf'           => 'application/vnd.epson.msf',
+    'qam'           => 'application/vnd.epson.quickanime',
+    'slt'           => 'application/vnd.epson.salt',
+    'ssf'           => 'application/vnd.epson.ssf',
+    'es3'           => 'application/vnd.eszigno3+xml',
+    'et3'           => 'application/vnd.eszigno3+xml',
+    'ez2'           => 'application/vnd.ezpix-album',
+    'ez3'           => 'application/vnd.ezpix-package',
+    'fdf'           => 'application/vnd.fdf',
+    'gph'           => 'application/vnd.flographit',
+    'ftc'           => 'application/vnd.fluxtime.clip',
+    'fm'            => 'application/vnd.framemaker',
+    'frame'         => 'application/vnd.framemaker',
+    'maker'         => 'application/vnd.framemaker',
+    'fnc'           => 'application/vnd.frogans.fnc',
+    'ltf'           => 'application/vnd.frogans.ltf',
+    'fsc'           => 'application/vnd.fsc.weblaunch',
+    'oas'           => 'application/vnd.fujitsu.oasys',
+    'oa2'           => 'application/vnd.fujitsu.oasys2',
+    'oa3'           => 'application/vnd.fujitsu.oasys3',
+    'fg5'           => 'application/vnd.fujitsu.oasysgp',
+    'bh2'           => 'application/vnd.fujitsu.oasysprs',
+    'ddd'           => 'application/vnd.fujixerox.ddd',
+    'xdw'           => 'application/vnd.fujixerox.docuworks',
+    'xbd'           => 'application/vnd.fujixerox.docuworks.binder',
+    'fzs'           => 'application/vnd.fuzzysheet',
+    'txd'           => 'application/vnd.genomatix.tuxedo',
+    'kml'           => 'application/vnd.google-earth.kml+xml',
+    'kmz'           => 'application/vnd.google-earth.kmz',
+    'gqf'           => 'application/vnd.grafeq',
+    'gqs'           => 'application/vnd.grafeq',
+    'gac'           => 'application/vnd.groove-account',
+    'ghf'           => 'application/vnd.groove-help',
+    'gim'           => 'application/vnd.groove-identity-message',
+    'grv'           => 'application/vnd.groove-injector',
+    'gtm'           => 'application/vnd.groove-tool-message',
+    'tpl'           => 'application/vnd.groove-tool-template',
+    'vcg'           => 'application/vnd.groove-vcard',
+    'zmm'           => 'application/vnd.handheld-entertainment+xml',
+    'hbci'          => 'application/vnd.hbci',
+    'les'           => 'application/vnd.hhe.lesson-player',
+    'hpgl'          => 'application/vnd.hp-hpgl',
+    'hpid'          => 'application/vnd.hp-hpid',
+    'hps'           => 'application/vnd.hp-hps',
+    'jlt'           => 'application/vnd.hp-jlyt',
+    'pcl'           => 'application/vnd.hp-pcl',
+    'pclxl'         => 'application/vnd.hp-pclxl',
+    'x3d'           => 'application/vnd.hzn-3d-crossword',
+    'mpy'           => 'application/vnd.ibm.minipay',
+    'afp'           => 'application/vnd.ibm.modcap',
+    'listafp'       => 'application/vnd.ibm.modcap',
+    'list3820'      => 'application/vnd.ibm.modcap',
+    'irm'           => 'application/vnd.ibm.rights-management',
+    'sc'            => 'application/vnd.ibm.secure-container',
+    'igl'           => 'application/vnd.igloader',
+    'ivp'           => 'application/vnd.immervision-ivp',
+    'ivu'           => 'application/vnd.immervision-ivu',
+    'xpw'           => 'application/vnd.intercon.formnet',
+    'xpx'           => 'application/vnd.intercon.formnet',
+    'qbo'           => 'application/vnd.intu.qbo',
+    'qfx'           => 'application/vnd.intu.qfx',
+    'rcprofile'     => 'application/vnd.ipunplugged.rcprofile',
+    'irp'           => 'application/vnd.irepository.package+xml',
+    'xpr'           => 'application/vnd.is-xpr',
+    'jam'           => 'application/vnd.jam',
+    'rms'           => 'application/vnd.jcp.javame.midlet-rms',
+    'jisp'          => 'application/vnd.jisp',
+    'joda'          => 'application/vnd.joost.joda-archive',
+    'ktz'           => 'application/vnd.kahootz',
+    'ktr'           => 'application/vnd.kahootz',
+    'karbon'        => 'application/x-karbon',
+    'chrt'          => 'application/x-kchart',
+    'kfo'           => 'application/x-kformula',
+    'flw'           => 'application/x-kivio',
+    'kon'           => 'application/x-kontour',
+    'kpr'           => 'application/x-kpresenter',
+    'kpt'           => 'application/x-kpresenter',
+    'ksp'           => 'application/x-kspread',
+    'kwd'           => 'application/x-kword',
+    'kwt'           => 'application/x-kword',
+    'htke'          => 'application/vnd.kenameaapp',
+    'kia'           => 'application/vnd.kidspiration',
+    'kne'           => 'application/vnd.kinar',
+    'knp'           => 'application/vnd.kinar',
+    'skp'           => 'application/vnd.koan',
+    'skd'           => 'application/vnd.koan',
+    'skt'           => 'application/vnd.koan',
+    'skm'           => 'application/vnd.koan',
+    'lbd'           => 'application/vnd.llamagraphics.life-balance.desktop',
+    'lbe'           => 'application/vnd.llamagraphics.life-balance.exchange+xml',
+    '123'           => 'application/vnd.lotus-1-2-3',
+    'apr'           => 'application/vnd.lotus-approach',
+    'pre'           => 'application/vnd.lotus-freelance',
+    'nsf'           => 'application/vnd.lotus-notes',
+    'org'           => 'application/vnd.lotus-organizer',
+    'scm'           => 'text/x-scheme',
+    'lwp'           => 'application/vnd.lotus-wordpro',
+    'portpkg'       => 'application/vnd.macports.portpkg',
+    'mcd'           => 'application/vnd.mcd',
+    'mc1'           => 'application/vnd.medcalcdata',
+    'cdkey'         => 'application/vnd.mediastation.cdkey',
+    'mwf'           => 'application/vnd.mfer',
+    'mfm'           => 'application/vnd.mfmp',
+    'flo'           => 'application/vnd.micrografx.flo',
+    'igx'           => 'application/vnd.micrografx.igx',
+    'mif'           => 'application/x-mif',
+    'daf'           => 'application/vnd.mobius.daf',
+    'dis'           => 'application/vnd.mobius.dis',
+    'mbk'           => 'application/vnd.mobius.mbk',
+    'mqy'           => 'application/vnd.mobius.mqy',
+    'msl'           => 'application/vnd.mobius.msl',
+    'plc'           => 'application/vnd.mobius.plc',
+    'txf'           => 'application/vnd.mobius.txf',
+    'mpn'           => 'application/vnd.mophun.application',
+    'mpc'           => 'application/vnd.mophun.certificate',
+    'xul'           => 'application/vnd.mozilla.xul+xml',
+    'cil'           => 'application/vnd.ms-artgalry',
+    'asf'           => 'video/x-ms-asf',
+    'cab'           => 'application/vnd.ms-cab-compressed',
+    'xls'           => 'application/vnd.ms-excel',
+    'xlm'           => 'application/vnd.ms-excel',
+    'xla'           => 'application/vnd.ms-excel',
+    'xlc'           => 'application/vnd.ms-excel',
+    'xlt'           => 'application/vnd.ms-excel',
+    'xlw'           => 'application/vnd.ms-excel',
+    'eot'           => 'application/vnd.ms-fontobject',
+    'chm'           => 'application/x-chm',
+    'ims'           => 'application/vnd.ms-ims',
+    'lrm'           => 'application/vnd.ms-lrm',
+    'ppt'           => 'application/vnd.ms-powerpoint',
+    'pps'           => 'application/vnd.ms-powerpoint',
+    'pot'           => 'text/x-gettext-translation-template',
+    'mpp'           => 'application/vnd.ms-project',
+    'mpt'           => 'application/vnd.ms-project',
+    'wps'           => 'application/vnd.ms-works',
+    'wks'           => 'application/vnd.lotus-1-2-3',
+    'wcm'           => 'application/vnd.ms-works',
+    'wdb'           => 'application/vnd.ms-works',
+    'wpl'           => 'application/vnd.ms-wpl',
+    'xps'           => 'application/vnd.ms-xpsdocument',
+    'mseq'          => 'application/vnd.mseq',
+    'mus'           => 'application/vnd.musician',
+    'msty'          => 'application/vnd.muvee.style',
+    'nlu'           => 'application/vnd.neurolanguage.nlu',
+    'nnd'           => 'application/vnd.noblenet-directory',
+    'nns'           => 'application/vnd.noblenet-sealer',
+    'nnw'           => 'application/vnd.noblenet-web',
+    'ngdat'         => 'application/vnd.nokia.n-gage.data',
+    'n-gage'        => 'application/vnd.nokia.n-gage.symbian.install',
+    'rpst'          => 'application/vnd.nokia.radio-preset',
+    'rpss'          => 'application/vnd.nokia.radio-presets',
+    'edm'           => 'application/vnd.novadigm.edm',
+    'edx'           => 'application/vnd.novadigm.edx',
+    'ext'           => 'application/vnd.novadigm.ext',
+    'odc'           => 'application/vnd.oasis.opendocument.chart',
+    'otc'           => 'application/vnd.oasis.opendocument.chart-template',
+    'odf'           => 'application/vnd.oasis.opendocument.formula',
+    'otf'           => 'application/vnd.oasis.opendocument.formula-template',
+    'odg'           => 'application/vnd.oasis.opendocument.graphics',
+    'otg'           => 'application/vnd.oasis.opendocument.graphics-template',
+    'odi'           => 'application/vnd.oasis.opendocument.image',
+    'oti'           => 'application/vnd.oasis.opendocument.image-template',
+    'odp'           => 'application/vnd.oasis.opendocument.presentation',
+    'otp'           => 'application/vnd.oasis.opendocument.presentation-template',
+    'ods'           => 'application/vnd.oasis.opendocument.spreadsheet',
+    'ots'           => 'application/vnd.oasis.opendocument.spreadsheet-template',
+    'odt'           => 'application/vnd.oasis.opendocument.text',
+    'otm'           => 'application/vnd.oasis.opendocument.text-master',
+    'ott'           => 'application/vnd.oasis.opendocument.text-template',
+    'oth'           => 'application/vnd.oasis.opendocument.text-web',
+    'xo'            => 'application/vnd.olpc-sugar',
+    'dd2'           => 'application/vnd.oma.dd2+xml',
+    'oxt'           => 'application/vnd.openofficeorg.extension',
+    'dp'            => 'application/vnd.osgi.dp',
+    'prc'           => 'application/vnd.palm',
+    'pdb'           => 'application/vnd.palm',
+    'pqa'           => 'application/vnd.palm',
+    'oprc'          => 'application/vnd.palm',
+    'str'           => 'application/vnd.pg.format',
+    'ei6'           => 'application/vnd.pg.osasli',
+    'efif'          => 'application/vnd.picsel',
+    'plf'           => 'application/vnd.pocketlearn',
+    'pbd'           => 'application/vnd.powerbuilder6',
+    'box'           => 'application/vnd.previewsystems.box',
+    'mgz'           => 'application/vnd.proteus.magazine',
+    'qps'           => 'application/vnd.publishare-delta-tree',
+    'ptid'          => 'application/vnd.pvi.ptid1',
+    'qxd'           => 'application/vnd.quark.quarkxpress',
+    'qxt'           => 'application/vnd.quark.quarkxpress',
+    'qwd'           => 'application/vnd.quark.quarkxpress',
+    'qwt'           => 'application/vnd.quark.quarkxpress',
+    'qxl'           => 'application/vnd.quark.quarkxpress',
+    'qxb'           => 'application/vnd.quark.quarkxpress',
+    'mxl'           => 'application/vnd.recordare.musicxml',
+    'rm'            => 'audio/x-pn-realaudio',
+    'see'           => 'application/vnd.seemail',
+    'sema'          => 'application/vnd.sema',
+    'semd'          => 'application/vnd.semd',
+    'semf'          => 'application/vnd.semf',
+    'ifm'           => 'application/vnd.shana.informed.formdata',
+    'itp'           => 'application/vnd.shana.informed.formtemplate',
+    'iif'           => 'application/vnd.shana.informed.interchange',
+    'ipk'           => 'application/vnd.shana.informed.package',
+    'twd'           => 'application/vnd.simtech-mindmapper',
+    'twds'          => 'application/vnd.simtech-mindmapper',
+    'mmf'           => 'application/vnd.smaf',
+    'sdkm'          => 'application/vnd.solent.sdkm+xml',
+    'sdkd'          => 'application/vnd.solent.sdkm+xml',
+    'dxp'           => 'application/vnd.spotfire.dxp',
+    'sfs'           => 'application/vnd.spotfire.sfs',
+    'sus'           => 'application/vnd.sus-calendar',
+    'susp'          => 'application/vnd.sus-calendar',
+    'svd'           => 'application/vnd.svd',
+    'xsm'           => 'application/vnd.syncml+xml',
+    'bdm'           => 'application/vnd.syncml.dm+wbxml',
+    'xdm'           => 'application/vnd.syncml.dm+xml',
+    'tao'           => 'application/vnd.tao.intent-module-archive',
+    'tmo'           => 'application/vnd.tmobile-livetv',
+    'tpt'           => 'application/vnd.trid.tpt',
+    'mxs'           => 'application/vnd.triscape.mxs',
+    'tra'           => 'application/vnd.trueapp',
+    'ufd'           => 'application/vnd.ufdl',
+    'ufdl'          => 'application/vnd.ufdl',
+    'utz'           => 'application/vnd.uiq.theme',
+    'umj'           => 'application/vnd.umajin',
+    'unityweb'      => 'application/vnd.unity',
+    'uoml'          => 'application/vnd.uoml+xml',
+    'vcx'           => 'application/vnd.vcx',
+    'vsd'           => 'application/vnd.visio',
+    'vst'           => 'application/vnd.visio',
+    'vss'           => 'application/vnd.visio',
+    'vsw'           => 'application/vnd.visio',
+    'vis'           => 'application/vnd.visionary',
+    'vsf'           => 'application/vnd.vsf',
+    'wbxml'         => 'application/vnd.wap.wbxml',
+    'wmlc'          => 'application/vnd.wap.wmlc',
+    'wmlsc'         => 'application/vnd.wap.wmlscriptc',
+    'wtb'           => 'application/vnd.webturbo',
+    'wpd'           => 'application/vnd.wordperfect',
+    'wqd'           => 'application/vnd.wqd',
+    'stf'           => 'application/vnd.wt.stf',
+    'xar'           => 'application/vnd.xara',
+    'xfdl'          => 'application/vnd.xfdl',
+    'hvd'           => 'application/vnd.yamaha.hv-dic',
+    'hvs'           => 'application/vnd.yamaha.hv-script',
+    'hvp'           => 'application/vnd.yamaha.hv-voice',
+    'saf'           => 'application/vnd.yamaha.smaf-audio',
+    'spf'           => 'application/vnd.yamaha.smaf-phrase',
+    'cmp'           => 'application/vnd.yellowriver-custom-menu',
+    'zaz'           => 'application/vnd.zzazz.deck+xml',
+    'vxml'          => 'application/voicexml+xml',
+    'hlp'           => 'application/winhlp',
+    'wsdl'          => 'application/wsdl+xml',
+    'wspolicy'      => 'application/wspolicy+xml',
+    'ace'           => 'application/x-ace-compressed',
+    'bcpio'         => 'application/x-bcpio',
+    'torrent'       => 'application/x-bittorrent',
+    'bz'            => 'application/x-bzip',
+    'bz2'           => 'application/x-bzip',
+    'boz'           => 'application/x-bzip2',
+    'vcd'           => 'application/x-cdlink',
+    'chat'          => 'application/x-chat',
+    'pgn'           => 'application/x-chess-pgn',
+    'cpio'          => 'application/x-cpio',
+    'csh'           => 'application/x-csh',
+    'dcr'           => 'application/x-director',
+    'dir'           => 'application/x-director',
+    'dxr'           => 'application/x-director',
+    'fgd'           => 'application/x-director',
+    'dvi'           => 'application/x-dvi',
+    'spl'           => 'application/x-futuresplash',
+    'gtar'          => 'application/x-gtar',
+    'hdf'           => 'application/x-hdf',
+    'latex'         => 'application/x-latex',
+    'wmd'           => 'application/x-ms-wmd',
+    'wmz'           => 'application/x-ms-wmz',
+    'mdb'           => 'application/x-msaccess',
+    'obd'           => 'application/x-msbinder',
+    'crd'           => 'application/x-mscardfile',
+    'clp'           => 'application/x-msclip',
+    'exe'           => 'application/x-ms-dos-executable',
+    'dll'           => 'application/x-msdownload',
+    'com'           => 'application/x-msdownload',
+    'bat'           => 'application/x-msdownload',
+    'msi'           => 'application/x-msdownload',
+    'mvb'           => 'application/x-msmediaview',
+    'm13'           => 'application/x-msmediaview',
+    'm14'           => 'application/x-msmediaview',
+    'wmf'           => 'image/x-wmf',
+    'mny'           => 'application/x-msmoney',
+    'pub'           => 'application/x-mspublisher',
+    'scd'           => 'application/x-msschedule',
+    'trm'           => 'application/x-msterminal',
+    'wri'           => 'application/x-mswrite',
+    'nc'            => 'application/x-netcdf',
+    'cdf'           => 'application/x-netcdf',
+    'p12'           => 'application/x-pkcs12',
+    'pfx'           => 'application/x-pkcs12',
+    'p7b'           => 'application/x-pkcs7-certificates',
+    'spc'           => 'application/x-pkcs7-certificates',
+    'p7r'           => 'application/x-pkcs7-certreqresp',
+    'rar'           => 'application/x-rar',
+    'sh'            => 'application/x-shellscript',
+    'shar'          => 'application/x-shar',
+    'swf'           => 'application/x-shockwave-flash',
+    'sit'           => 'application/stuffit',
+    'sitx'          => 'application/x-stuffitx',
+    'sv4cpio'       => 'application/x-sv4cpio',
+    'sv4crc'        => 'application/x-sv4crc',
+    'tar'           => 'application/x-tar',
+    'tcl'           => 'text/x-tcl',
+    'tex'           => 'text/x-tex',
+    'texinfo'       => 'text/x-texinfo',
+    'texi'          => 'text/x-texinfo',
+    'ustar'         => 'application/x-ustar',
+    'src'           => 'application/x-wais-source',
+    'der'           => 'application/x-x509-ca-cert',
+    'crt'           => 'application/x-x509-ca-cert',
+    'xenc'          => 'application/xenc+xml',
+    'xhtml'         => 'application/xhtml+xml',
+    'xht'           => 'application/xhtml+xml',
+    'xml'           => 'text/xml',
+    'xsl'           => 'text/x-xslt',
+    'dtd'           => 'text/x-dtd',
+    'xop'           => 'application/xop+xml',
+    'xslt'          => 'text/x-xslt',
+    'xspf'          => 'application/xspf+xml',
+    'mxml'          => 'application/xv+xml',
+    'xhvml'         => 'application/xv+xml',
+    'xvml'          => 'application/xv+xml',
+    'xvm'           => 'application/xv+xml',
+    'zip'           => 'application/zip',
+    'au'            => 'audio/basic',
+    'snd'           => 'audio/basic',
+    'mid'           => 'audio/midi',
+    'midi'          => 'audio/midi',
+    'kar'           => 'audio/midi',
+    'rmi'           => 'audio/midi',
+    'mp4a'          => 'audio/mp4',
+    'mpga'          => 'audio/mpeg',
+    'mp2'           => 'video/mpeg',
+    'mp2a'          => 'audio/mpeg',
+    'mp3'           => 'audio/mpeg',
+    'm2a'           => 'audio/mpeg',
+    'm3a'           => 'audio/mpeg',
+    'eol'           => 'audio/vnd.digital-winds',
+    'lvp'           => 'audio/vnd.lucent.voice',
+    'ecelp4800'     => 'audio/vnd.nuera.ecelp4800',
+    'ecelp7470'     => 'audio/vnd.nuera.ecelp7470',
+    'ecelp9600'     => 'audio/vnd.nuera.ecelp9600',
+    'wav'           => 'audio/x-wav',
+    'aif'           => 'audio/x-aiff',
+    'aiff'          => 'audio/x-aiff',
+    'aifc'          => 'audio/x-aiff',
+    'm3u'           => 'audio/x-mpegurl',
+    'wax'           => 'audio/x-ms-wax',
+    'wma'           => 'audio/x-ms-wma',
+    'ram'           => 'audio/x-pn-realaudio',
+    'ra'            => 'audio/x-pn-realaudio',
+    'rmp'           => 'audio/x-pn-realaudio-plugin',
+    'cdx'           => 'chemical/x-cdx',
+    'cif'           => 'chemical/x-cif',
+    'cmdf'          => 'chemical/x-cmdf',
+    'cml'           => 'chemical/x-cml',
+    'csml'          => 'chemical/x-csml',
+    'xyz'           => 'chemical/x-xyz',
+    'bmp'           => 'image/bmp',
+    'cgm'           => 'image/cgm',
+    'g3'            => 'image/fax-g3',
+    'gif'           => 'image/gif',
+    'ief'           => 'image/ief',
+    'jpg'           => 'image/jpeg',
+    'jpeg'          => 'image/jpeg',
+    'jpe'           => 'image/jpeg',
+    'png'           => 'image/png',
+    'btif'          => 'image/prs.btif',
+    'svg'           => 'image/svg+xml',
+    'svgz'          => 'image/svg+xml',
+    'tiff'          => 'image/tiff',
+    'tif'           => 'image/tiff',
+    'psd'           => 'image/x-psd',
+    'djvu'          => 'image/vnd.djvu',
+    'djv'           => 'image/vnd.djvu',
+    'dwg'           => 'image/vnd.dwg',
+    'dxf'           => 'image/vnd.dxf',
+    'fbs'           => 'image/vnd.fastbidsheet',
+    'fpx'           => 'image/vnd.fpx',
+    'fst'           => 'image/vnd.fst',
+    'mmr'           => 'image/vnd.fujixerox.edmics-mmr',
+    'rlc'           => 'image/vnd.fujixerox.edmics-rlc',
+    'mdi'           => 'image/vnd.ms-modi',
+    'npx'           => 'image/vnd.net-fpx',
+    'wbmp'          => 'image/vnd.wap.wbmp',
+    'xif'           => 'image/vnd.xiff',
+    'ras'           => 'image/x-cmu-raster',
+    'cmx'           => 'image/x-cmx',
+    'ico'           => 'image/x-ico',
+    'pcx'           => 'image/x-pcx',
+    'pic'           => 'image/x-pict',
+    'pct'           => 'image/x-pict',
+    'pnm'           => 'image/x-portable-anymap',
+    'pbm'           => 'image/x-portable-bitmap',
+    'pgm'           => 'image/x-portable-graymap',
+    'ppm'           => 'image/x-portable-pixmap',
+    'rgb'           => 'image/x-rgb',
+    'xbm'           => 'image/x-xbitmap',
+    'xpm'           => 'image/x-xpixmap',
+    'xwd'           => 'image/x-xwindowdump',
+    'eml'           => 'message/rfc822',
+    'mime'          => 'message/rfc822',
+    'igs'           => 'model/iges',
+    'iges'          => 'model/iges',
+    'msh'           => 'model/mesh',
+    'mesh'          => 'model/mesh',
+    'silo'          => 'model/mesh',
+    'dwf'           => 'model/vnd.dwf',
+    'gdl'           => 'model/vnd.gdl',
+    'gtw'           => 'model/vnd.gtw',
+    'mts'           => 'model/vnd.mts',
+    'vtu'           => 'model/vnd.vtu',
+    'wrl'           => 'model/vrml',
+    'vrml'          => 'model/vrml',
+    'ics'           => 'text/calendar',
+    'ifb'           => 'text/calendar',
+    'css'           => 'text/css',
+    'csv'           => 'text/x-comma-separated-values',
+    'html'          => 'text/html',
+    'htm'           => 'text/html',
+    'txt'           => 'text/plain',
+    'text'          => 'text/plain',
+    'conf'          => 'text/plain',
+    'def'           => 'text/plain',
+    'list'          => 'text/plain',
+    'log'           => 'text/x-log',
+    'in'            => 'text/plain',
+    'dsc'           => 'text/prs.lines.tag',
+    'rtx'           => 'text/richtext',
+    'sgml'          => 'text/sgml',
+    'sgm'           => 'text/sgml',
+    'tsv'           => 'text/tab-separated-values',
+    't'             => 'application/x-troff',
+    'tr'            => 'application/x-troff',
+    'roff'          => 'application/x-troff',
+    'man'           => 'application/x-troff-man',
+    'me'            => 'text/x-troff-me',
+    'ms'            => 'text/x-troff-ms',
+    'uri'           => 'text/x-uri',
+    'uris'          => 'text/uri-list',
+    'urls'          => 'text/uri-list',
+    'fly'           => 'text/vnd.fly',
+    'flx'           => 'text/vnd.fmi.flexstor',
+    '3dml'          => 'text/vnd.in3d.3dml',
+    'spot'          => 'text/vnd.in3d.spot',
+    'jad'           => 'text/vnd.sun.j2me.app-descriptor',
+    'wml'           => 'text/vnd.wap.wml',
+    'wmls'          => 'text/vnd.wap.wmlscript',
+    's'             => 'text/x-asm',
+    'asm'           => 'text/x-asm',
+    'c'             => 'text/x-csrc',
+    'cc'            => 'text/x-c++src',
+    'cxx'           => 'text/x-c++src',
+    'cpp'           => 'text/x-c++src',
+    'h'             => 'text/x-chdr',
+    'hh'            => 'text/x-c++hdr',
+    'dic'           => 'text/x-c',
+    'f'             => 'text/x-fortran',
+    'for'           => 'text/x-fortran',
+    'f77'           => 'text/x-fortran',
+    'f90'           => 'text/x-fortran',
+    'p'             => 'text/x-pascal',
+    'pas'           => 'text/x-pascal',
+    'java'          => 'text/x-java',
+    'etx'           => 'text/x-setext',
+    'uu'            => 'text/x-uuencode',
+    'vcs'           => 'text/calendar',
+    'vcf'           => 'text/directory',
+    '3gp'           => 'video/3gpp',
+    '3g2'           => 'video/3gpp2',
+    'h261'          => 'video/h261',
+    'h263'          => 'video/h263',
+    'h264'          => 'video/h264',
+    'jpgv'          => 'video/jpeg',
+    'jpm'           => 'video/jpm',
+    'jpgm'          => 'video/jpm',
+    'mj2'           => 'video/mj2',
+    'mjp2'          => 'video/mj2',
+    'mp4'           => 'video/mp4',
+    'mp4v'          => 'video/mp4',
+    'mpg4'          => 'video/mp4',
+    'mpeg'          => 'video/mpeg',
+    'mpg'           => 'video/mpeg',
+    'mpe'           => 'video/mpeg',
+    'm1v'           => 'video/mpeg',
+    'm2v'           => 'video/mpeg',
+    'qt'            => 'video/quicktime',
+    'mov'           => 'video/quicktime',
+    'fvt'           => 'video/vnd.fvt',
+    'mxu'           => 'video/vnd.mpegurl',
+    'm4u'           => 'video/vnd.mpegurl',
+    'viv'           => 'video/vnd.vivo',
+    'fli'           => 'video/x-flic',
+    'asx'           => 'video/x-ms-asf',
+    'wm'            => 'video/x-ms-wm',
+    'wmv'           => 'video/x-ms-wmv',
+    'wmx'           => 'video/x-ms-wmx',
+    'wvx'           => 'video/x-ms-wvx',
+    'avi'           => 'video/x-msvideo',
+    'movie'         => 'video/x-sgi-movie',
+    'ice'           => 'x-conference/x-cooltalk',
+    'odb'           => 'application/vnd.oasis.opendocument.database',
+    'oot'           => 'application/vnd.oasis.opendocument.text',
+    'odm'           => 'application/vnd.oasis.opendocument.text-master',
+    'sxc'           => 'application/vnd.sun.xml.calc',
+    'stc'           => 'application/vnd.sun.xml.calc.template',
+    'sxd'           => 'application/vnd.sun.xml.draw',
+    'std'           => 'application/vnd.sun.xml.draw.template',
+    'sxg'           => 'application/vnd.sun.xml.writer.global',
+    'sxm'           => 'application/vnd.sun.xml.math',
+    'sxi'           => 'application/vnd.sun.xml.impress',
+    'sti'           => 'application/vnd.sun.xml.impress.template',
+    'sxw'           => 'application/vnd.sun.xml.writer',
+    'stw'           => 'application/vnd.sun.xml.writer.template',
+    'Z'             => 'application/x-compress',
+    'gz'            => 'application/x-gzip',
+    'tgz'           => 'application/x-compressed-tar',
+    'php'           => 'application/x-php',
+    'php3'          => 'application/x-php',
+    'pl'            => 'application/x-perl',
+    'pm'            => 'application/x-perl',
+    'gsm'           => 'audio/x-gsm',
+    'vfb'           => 'text/calendar',
+    'diff'          => 'text/x-patch',
+    'patch'         => 'text/x-patch',
+    'shtml'         => 'text/html',
+    'po'            => 'text/x-gettext-translation',
+    'sgl'           => 'application/vnd.stardivision.writer',
+    'wk4'           => 'application/vnd.lotus-1-2-3',
+    'pict2'         => 'image/x-pict',
+    'lhz'           => 'application/x-lhz',
+    'tar.bz2'       => 'application/x-bzip-compressed-tar',
+    'rle'           => 'image/rle',
+    'pcf.Z'         => 'application/x-font-type1',
+    'm15'           => 'audio/x-mod',
+    'flac'          => 'audio/x-flac',
+    'dc'            => 'application/x-dc-rom',
+    'm'             => 'text/x-objcsrc',
+    'o'             => 'application/x-object',
+    'fits'          => 'image/x-fits',
+    'pfa'           => 'application/x-font-type1',
+    'jnlp'          => 'application/x-java-jnlp-file',
+    'pfb'           => 'application/x-font-type1',
+    'smd'           => 'application/vnd.stardivision.mail',
+    'it'            => 'audio/x-it',
+    'bib'           => 'text/x-bibtex',
+    'moc'           => 'text/x-moc',
+    'theme'         => 'application/x-theme',
+    'mod'           => 'audio/x-mod',
+    'smf'           => 'application/vnd.stardivision.math',
+    'uni'           => 'audio/x-mod',
+    'mtm'           => 'audio/x-mod',
+    'ppz'           => 'application/vnd.ms-powerpoint',
+    's3m'           => 'audio/x-s3m',
+    'deb'           => 'application/x-deb',
+    'tk'            => 'text/x-tcl',
+    'cdr'           => 'application/vnd.corel-draw',
+    'lwob'          => 'image/x-lwo',
+    'sml'           => 'application/smil',
+    'etheme'        => 'application/x-e-theme',
+    '3ds'           => 'image/x-3ds',
+    'vob'           => 'video/mpeg',
+    'voc'           => 'audio/x-voc',
+    'bdf'           => 'application/x-font-bdf',
+    'ps.gz'         => 'application/x-gzpostscript',
+    'uil'           => 'text/x-uil',
+    'ts'            => 'application/x-linguist',
+    'asp'           => 'application/x-asp',
+    'nes'           => 'application/x-nes-rom',
+    'sms'           => 'application/x-sms-rom',
+    'BLEND'         => 'application/x-blender',
+    'kil'           => 'application/x-killustrator',
+    'icb'           => 'image/x-icb',
+    'lyx'           => 'application/x-lyx',
+    'jng'           => 'image/x-jng',
+    'vor'           => 'application/vnd.stardivision.writer',
+    'adb'           => 'text/x-adasrc',
+    'flc'           => 'video/x-flic',
+    'wpg'           => 'application/x-wpg',
+    'wb1'           => 'application/x-quattropro',
+    'gra'           => 'application/x-graphite',
+    'wb2'           => 'application/x-quattropro',
+    'ltx'           => 'text/x-tex',
+    'xac'           => 'application/x-gnucash',
+    'wb3'           => 'application/x-quattropro',
+    'epsf'          => 'image/x-eps',
+    'el'            => 'text/x-emacs-lisp',
+    'jp2'           => 'image/jpeg2000',
+    'tar.gz'        => 'application/x-compressed-tar',
+    'epsi'          => 'image/x-eps',
+    'ui'            => 'application/x-designer',
+    'old'           => 'application/x-trash',
+    'tar.Z'         => 'application/x-tarz',
+    'ttf'           => 'application/x-font-ttf',
+    'siag'          => 'application/x-siag',
+    'sid'           => 'audio/prs.sid',
+    'msod'          => 'image/x-msod',
+    'h++'           => 'text/x-chdr',
+    'tar.lzo'       => 'application/x-tzo',
+    'tar.bz'        => 'application/x-bzip-compressed-tar',
+    'ads'           => 'text/x-adasrc',
+    'sda'           => 'application/vnd.stardivision.draw',
+    'lzo'           => 'application/x-lzop',
+    'cur'           => 'image/x-win-bitmap',
+    'sdc'           => 'application/vnd.stardivision.calc',
+    'sik'           => 'application/x-trash',
+    'sdd'           => 'application/vnd.stardivision.impress',
+    'xld'           => 'application/vnd.ms-excel',
+    'gmo'           => 'application/x-gettext-translation',
+    'xll'           => 'application/vnd.ms-excel',
+    'blend'         => 'application/x-blender',
+    'pw'            => 'application/x-pw',
+    'kud'           => 'application/x-kugar',
+    'mkv'           => 'application/x-matroska',
+    'obj'           => 'application/x-tgif',
+    'py'            => 'text/x-python',
+    'sds'           => 'application/vnd.stardivision.chart',
+    'idl'           => 'text/x-idl',
+    'dat'           => 'video/mpeg',
+    'stm'           => 'audio/x-stm',
+    'PAR2'          => 'application/x-par2',
+    'xcf.bz2'       => 'image/x-compressed-xcf',
+    'psid'          => 'audio/prs.sid',
+    'pict'          => 'image/x-pict',
+    'ag'            => 'image/x-applix-graphics',
+    'fo'            => 'text/x-xslfo',
+    'sdw'           => 'application/vnd.stardivision.writer',
+    'abw.CRASHED'   => 'application/x-abiword',
+    'gsf'           => 'application/x-font-type1',
+    'xcf.gz'        => 'image/x-compressed-xcf',
+    'pcd'           => 'image/x-photo-cd',
+    'egon'          => 'application/x-egon',
+    'pcf'           => 'application/x-font-pcf',
+    'al'            => 'application/x-perl',
+    'gnc'           => 'application/x-gnucash',
+    'tzo'           => 'application/x-tzo',
+    'la'            => 'application/x-shared-library-la',
+    'kpm'           => 'application/x-kpovmodeler',
+    'qif'           => 'application/x-qw',
+    'sty'           => 'text/x-tex',
+    'psf'           => 'application/x-font-linux-psf',
+    'as'            => 'application/x-applix-spreadsheet',
+    'dbf'           => 'application/x-dbase',
+    'ilbm'          => 'image/x-ilbm',
+    'aw'            => 'application/x-applix-word',
+    'gb'            => 'application/x-gameboy-rom',
+    'xmi'           => 'text/x-xmi',
+    'abw.gz'        => 'application/x-abiword',
+    'XM'            => 'audio/x-mod',
+    'gnumeric'      => 'application/x-gnumeric',
+    'bak'           => 'application/x-trash',
+    'xslfo'         => 'text/x-xslfo',
+    'gg'            => 'application/x-sms-rom',
+    'cgi'           => 'application/x-cgi',
+    'mgp'           => 'application/x-magicpoint',
+    'spd'           => 'application/x-font-speedo',
+    'gnucash'       => 'application/x-gnucash',
+    'cls'           => 'text/x-tex',
+    'php4'          => 'application/x-php',
+    'sun'           => 'image/x-sun-raster',
+    'pyc'           => 'application/x-python-bytecode',
+    'xcf'           => 'image/x-xcf',
+    'xbel'          => 'application/x-xbel',
+    'jpr'           => 'application/x-jbuilder-project',
+    'afm'           => 'application/x-font-afm',
+    'fig'           => 'image/x-xfig',
+    'perl'          => 'application/x-perl',
+    'rej'           => 'application/x-reject',
+    'qtvr'          => 'video/quicktime',
+    'jpx'           => 'application/x-jbuilder-project',
+    '669'           => 'audio/x-mod',
+    'kdelnk'        => 'application/x-desktop',
+    'md'            => 'application/x-genesis-rom',
+    'pyo'           => 'application/x-python-bytecode',
+    'oleo'          => 'application/x-oleo',
+    'ac3'           => 'audio/ac3',
+    'mml'           => 'text/mathml',
+    'par2'          => 'application/x-par2',
+    'sylk'          => 'text/spreadsheet',
+    'C'             => 'text/x-c++src',
+    'cert'          => 'application/x-x509-ca-cert',
+    'ult'           => 'audio/x-mod',
+    'lwo'           => 'image/x-lwo',
+    'dcl'           => 'text/x-dcl',
+    'zoo'           => 'application/x-zoo',
+    'dcm'           => 'application/dicom',
+    'mm'            => 'text/x-troff-mm',
+    'iff'           => 'image/x-iff',
+    'lws'           => 'image/x-lws',
+    'zabw'          => 'application/x-abiword',
+    'blender'       => 'application/x-blender',
+    'glade'         => 'application/x-glade',
+    'rpm'           => 'application/x-rpm',
+    'tga'           => 'image/x-tga',
+    'jar'           => 'application/x-jar',
+    'cpio.gz'       => 'application/x-cpio-compressed',
+    'dsl'           => 'text/x-dsl',
+    'kra'           => 'application/x-krita',
+    'n64'           => 'application/x-n64-rom',
+    'm4a'           => 'audio/x-m4a',
+    'c++'           => 'text/x-c++src',
+    'moov'          => 'video/quicktime',
+    'sam'           => 'application/x-amipro',
+    'nsv'           => 'video/x-nsv',
+    'dia'           => 'application/x-dia-diagram',
+    'xi'            => 'audio/x-xi',
+    'hp'            => 'text/x-chdr',
+    'gen'           => 'application/x-genesis-rom',
+    'url'           => 'text/x-uri',
+    'hs'            => 'text/x-haskell',
+    'xm'            => 'audio/x-xm',
+    'sql'           => 'text/x-sql',
+    'NSV'           => 'video/x-nsv',
+    'desktop'       => 'application/x-desktop',
+    'mng'           => 'video/x-mng',
+    'pem'           => 'application/x-x509-ca-cert',
+    'slk'           => 'text/spreadsheet',
+    'cs'            => 'text/x-csharp',
+    'arj'           => 'application/x-arj',
+    'a'             => 'application/x-archive',
+    'lhs'           => 'text/x-literate-haskell',
+    'gcrd'          => 'text/directory',
+    'vct'           => 'text/directory',
+    'wk1'           => 'application/vnd.lotus-1-2-3',
+    'msx'           => 'application/x-msx-rom',
+    'sgi'           => 'image/x-sgi',
+    'd'             => 'text/x-dsrc',
+    'CSSL'          => 'text/css',
+    'wk3'           => 'application/vnd.lotus-1-2-3',
+    'abw'           => 'application/x-abiword',
+    'pict1'         => 'image/x-pict'
+);
\ No newline at end of file