--- /dev/null
+<?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);
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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);
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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 parameters.
+ *
+ * @var array
+ */
+ protected $_conf = array();
+
+ /**
+ * The Horde_MIME_Part object to render.
+ *
+ * @var Horde_MIME_Part
+ */
+ protected $_mimepart = null;
+
+ /**
+ * The MIME display type for this part.
+ *
+ * @var string
+ */
+ protected $_type = null;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Return the MIME type of the rendered content. This can be
+ * overridden by the individual drivers, depending on what format
+ * they output in. By default, it passes through the MIME type of
+ * the object, or replaces custom extension types with
+ * 'text/plain' to let the browser do a best-guess render.
+ *
+ * @return string MIME type of the output content.
+ */
+ public function getType()
+ {
+ if (is_null($this->_type)) {
+ return is_null(is_null($this->_mimepart) || ($this->_mimepart->getPrimaryType() == 'x-extension'))
+ ? 'text/plain'
+ : $this->_mimepart->getType(true);
+ }
+
+ return $this->_type;
+ }
+
+ /**
+ * Return the rendered version of the Horde_MIME_Part object.
+ *
+ * @return string Rendered version of the Horde_MIME_Part object.
+ */
+ public function render()
+ {
+ return (is_null($this->_mimepart) || !$this->canDisplay())
+ ? ''
+ : $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;
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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]);
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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
+{
+}
--- /dev/null
+<?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('/(?<!<)<bold.*>(.*)<\/bold>/Uis', '<span style="font-weight: bold">\1</span>', $text);
+ $text = preg_replace('/(?<!<)<italic.*>(.*)<\/italic>/Uis', '<span style="font-style: italic">\1</span>', $text);
+ $text = preg_replace('/(?<!<)<underline.*>(.*)<\/underline>/Uis', '<span style="text-decoration: underline">\1</span>', $text);
+ $text = preg_replace_callback('/(?<!<)<color r=([\da-fA-F]+) g=([\da-fA-F]+) b=([\da-fA-F]+)>(.*)<\/color>/Uis', array($this, 'colorize'), $text);
+ $text = preg_replace('/(?<!<)<color n=(red|blue|green|yellow|cyan|magenta|black|white)>(.*)<\/color>/Uis', '<span style="color: \1">\2</span>', $text);
+ $text = preg_replace('/(?<!<)<fontfamily>(.*)<\/fontfamily>/Uis', '\1', $text);
+ $text = preg_replace('/(?<!<)<fontfamily f=(\w+)>(.*)<\/fontfamily>/Uis', '<span style="font-family: \1">\2</span>', $text);
+ $text = preg_replace('/(?<!<)<smaller.*>/Uis', '<span style="font-size: smaller">', $text);
+ $text = preg_replace('/(?<!<)<\/smaller>/Uis', '</span>', $text);
+ $text = preg_replace('/(?<!<)<bigger.*>/Uis', '<span style="font-size: larger">', $text);
+ $text = preg_replace('/(?<!<)<\/bigger>/Uis', '</span>', $text);
+ $text = preg_replace('/(?<!<)<fixed.*>(.*)<\/fixed>/Uis', '<font face="fixed">\1</font>', $text);
+ $text = preg_replace('/(?<!<)<center.*>(.*)<\/center>/Uis', '<div align="center">\1</div>', $text);
+ $text = preg_replace('/(?<!<)<flushleft.*>(.*)<\/flushleft>/Uis', '<div align="left">\1</div>', $text);
+ $text = preg_replace('/(?<!<)<flushright.*>(.*)<\/flushright>/Uis', '<div align="right">\1</div>', $text);
+ $text = preg_replace('/(?<!<)<flushboth.*>(.*)<\/flushboth>/Uis', '<div align="justify">\1</div>', $text);
+ $text = preg_replace('/(?<!<)<paraindent.*>(.*)<\/paraindent>/Uis', '<blockquote>\1</blockquote>', $text);
+ $text = preg_replace('/(?<!<)<excerpt.*>(.*)<\/excerpt>/Uis', '<blockquote>\1</blockquote>', $text);
+
+ // Replace << with < now (from translated HTML form).
+ $text = str_replace('<<', '<', $text);
+
+ // Now we remove the leading/trailing space we added at the
+ // start.
+ $text = preg_replace('/^ (.*) $/s', '\1', $text);
+
+ // Make URLs clickable.
+ require_once 'Horde/Text/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(' ', ' ', $text);
+ $text = str_replace("\n ", "\n ", $text);
+ if ($text[0] == ' ') {
+ $text = ' ' . substr($text, 1);
+ }
+ $text = nl2br($text);
+ $text = '<p class="fixed">' . $text . '</p>';
+
+ return $text;
+ }
+
+ /**
+ */
+ 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();
+ }
+}
--- /dev/null
+<?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;
+ }
+ }
+}
--- /dev/null
+<?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
+{
+ /**
+ * 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)
+ {
+ return $this->_cleanHTML($this->mime_part->getContents());
+ }
+
+ /**
+ * 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.
+ *
+ * @return string The cleaned HTML data.
+ */
+ protected function _cleanHTML($data)
+ {
+ global $browser, $prefs;
+
+ $inline = $this->viewInline();
+ $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, 1) != '/') {
+ $base .= '/';
+ }
+
+ /* Recursively call _cleanHTML() to prevent clever fiends from
+ * sneaking nasty things into the page via $base. */
+ $base = $this->_cleanHTML($base);
+ }
+
+ /* 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. */
+ $links = count($m[0]);
+ for ($i = 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->mime_part->getCharset()) . '</span><br />';
+ }
+ $phish_warning = $this->formatStatusMsg($phish_warning, null, 'mimeStatusWarning');
+ if (stristr($data, '<body') === false) {
+ $data = $phish_warning . $data;
+ } else {
+ $data = preg_replace('/(.*<body.*?>)(.*)/i', '$1' . $phish_warning . '$2', $data);
+ }
+ }
+
+ return $data;
+ }
+
+ /**
+ */
+ protected function _dereferExternalReferencesCallback($m)
+ {
+ return 'href="' . Horde::externalUrl($m[2]) . '"';
+ }
+
+ /**
+ * Return the content-type of the rendered text.
+ *
+ * @return string The MIME Content-Type.
+ */
+ public function getType()
+ {
+ return $this->viewAsAttachment() ? $this->mime_part->getType(true) : 'text/html; charset=' . NLS::getCharset();
+ }
+}
--- /dev/null
+<?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 '&' entities to '&' for JS URL links. */
+ $url = str_replace('&', '&', $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;
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<!--
+
+ 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> </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>    </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>    </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>    </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>	</xsl:text><xsl:for-each select="following-sibling::text:tab-stop"><xsl:text>	</xsl:text></xsl:for-each></xsl:element>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="text:tab-stop"><xsl:text>	</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>
--- /dev/null
+ <!--
+
+ 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() < $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 < 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 < 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() < $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() < $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
--- /dev/null
+<!--
+
+ 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>
--- /dev/null
+<!--
+
+ 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 : &date &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>
--- /dev/null
+<!--
+
+ 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>
--- /dev/null
+<!--
+
+ 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>
--- /dev/null
+<!--
+
+ 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>
--- /dev/null
+<!--
+
+ 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>
--- /dev/null
+<!--
+
+ 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>  </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>
+
+ <!--   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::*)">  </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>
--- /dev/null
+<!--
+
+ 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 < 5)"> ???-->
+ <xsl:when test="position() = last() or position() = (last() - 1)">
+ <xsl:if test="@table:number-columns-repeated < 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>
--- /dev/null
+<!--
+
+ 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>
--- /dev/null
+<?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';
+ }
+}
--- /dev/null
+<?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('<?php ', '', 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(' ',
+ '&',
+ '<br />',
+ '<span style="color: ',
+ '<font color="',
+ '</font>',
+ ),
+ array(' ',
+ '&',
+ "\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 = ' ';
+ }
+
+ 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);
+ }
+}
--- /dev/null
+<?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
+{
+ /**
+ * Constructor.
+ *
+ * @param array $conf Configuration specific to the driver.
+ */
+ function __construct($conf = array())
+ {
+ $this->_canrender['full'] = true;
+ $this->_canrender['inline'] = true;
+ $this->_type = 'text/html; charset=' . NLS::getCharset();
+ parent::__construct($conf);
+ }
+
+ /**
+ * Render out the contents.
+ *
+ * @return string The rendered contents.
+ */
+ protected function _render()
+ {
+ $text = $this->_mimepart->getContents();
+
+ /* 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 '<html><body><tt>' . Text_Filter::filter($text, 'text2html', array('parselevel' => TEXT_HTML_MICRO, 'charset' => NLS::getCharset(), 'class' => null)) . '</tt></body></html>';
+ }
+
+ /**
+ * Render out the contents.
+ *
+ * @return string The rendered contents.
+ */
+ protected function _renderInline()
+ {
+ $text = $this->_mimepart->getContents();
+
+ /* 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();
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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('<', ' '), $text);
+ } else {
+ $text = preg_replace(array('/<lt>/i', "/\r\n/"), array('<', ' '), $text);
+ }
+
+ /* We try to protect against bad stuff here. */
+ $text = @htmlspecialchars($text, ENT_QUOTES, $this->mime_part->getCharset());
+
+ /* <nl> becomes a newline (<br />);
+ * <np> becomes a paragraph break (<p />). */
+ if ($has_str_ireplace) {
+ $text = str_ireplace(array('<nl>', '<np>'), array('<br />', '<p />'), $text);
+ } else {
+ $text = preg_replace(array('/(?<!<)<nl.*>/Uis', '/(?<!<)<np.*>/Uis'), array('<br />', '<p />'), $text);
+ }
+
+ /* Now convert the known tags to html. Try to remove any tag
+ * parameters to stop people from trying to pull a fast one. */
+ $pattern = array(
+ '/(?<!<)<bold.*>(.*)<\/bold>/Uis',
+ '/(?<!<)<italic.*>(.*)<\/italic>/Uis',
+ '/(?<!<)<fixed.*>(.*)<\/fixed>/Uis',
+ '/(?<!<)<smaller.*>(.*)<\/smaller>/Uis',
+ '/(?<!<)<bigger.*>(.*)<\/bigger>/Uis',
+ '/(?<!<)<underline.*>(.*)<\/underline>/Uis',
+ '/(?<!<)<center.*>(.*)<\/center>/Uis',
+ '/(?<!<)<flushleft.*>(.*)<\/flushleft>/Uis',
+ '/(?<!<)<flushright.*>(.*)<\/flushright>/Uis',
+ '/(?<!<)<indent.*>(.*)<\/indent>/Uis',
+ '/(?<!<)<excerpt.*>(.*)<\/excerpt>/Uis',
+ '/(?<!<)<subscript.*>(.*)<\/subscript>/Uis',
+ '/(?<!<)<superscript.*>(.*)<\/superscript>/Uis',
+ '/(?<!<)<heading.*>(.*)<\/heading>/Uis',
+ '/(?<!<)<footing.*>(.*)<\/footing>/Uis',
+ '/(?<!<)<paragraph.*>(.*)<\/paragraph>/Uis',
+ '/(?<!<)<signature.*>(.*)<\/signature>/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(' ', ' ', "\n "), $text);
+ if ($text[0] == ' ') {
+ $text = ' ' . 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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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;
+ }
+}
--- /dev/null
+<?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';
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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');
+ }
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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"> </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> </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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?php
+/**
+ * This file contains a mapping of common file extensions to
+ * MIME types. It has been automatically generated from the
+ * horde/scripts/mime_mapping directory.
+ *
+ * ALL changes should be made to horde/scripts/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.17 2008/10/31 18:00:04 slusarz Exp $
+ *
+ * Generated: 10/31/08 11:59:02 by slusarz on bigworm.curecanti.org
+ */
+$mime_extension_map = array(
+ '__MAXPERIOD__' => '1',
+ '3dml' => 'text/vnd.in3d.3dml',
+ '3ds' => 'image/x-3ds',
+ '3g2' => 'video/3gpp2',
+ '3gp' => 'video/3gpp',
+ 'BLEND' => 'application/x-blender',
+ 'C' => 'text/x-c++src',
+ 'CSSL' => 'text/css',
+ 'NSV' => 'video/x-nsv',
+ 'PAR2' => 'application/x-par2',
+ 'XM' => 'audio/x-mod',
+ 'Z' => 'application/x-compress',
+ 'a' => 'application/x-archive',
+ 'abw' => 'application/x-abiword',
+ 'abw.CRASHED' => 'application/x-abiword',
+ 'abw.gz' => 'application/x-abiword',
+ 'ac3' => 'audio/ac3',
+ 'ace' => 'application/x-ace-compressed',
+ 'acu' => 'application/vnd.acucobol',
+ 'acutc' => 'application/vnd.acucorp',
+ 'adb' => 'text/x-adasrc',
+ 'ads' => 'text/x-adasrc',
+ 'aep' => 'application/vnd.audiograph',
+ 'afm' => 'application/x-font-afm',
+ 'afp' => 'application/vnd.ibm.modcap',
+ 'ag' => 'image/x-applix-graphics',
+ 'ai' => 'application/illustrator',
+ 'aif' => 'audio/x-aiff',
+ 'aifc' => 'audio/x-aiff',
+ 'aiff' => 'audio/x-aiff',
+ 'al' => 'application/x-perl',
+ 'ami' => 'application/vnd.amiga.ami',
+ 'apr' => 'application/vnd.lotus-approach',
+ 'arj' => 'application/x-arj',
+ 'as' => 'application/x-applix-spreadsheet',
+ 'asc' => 'text/plain',
+ 'asf' => 'video/x-ms-asf',
+ 'asm' => 'text/x-asm',
+ 'aso' => 'application/vnd.accpac.simply.aso',
+ 'asp' => 'application/x-asp',
+ 'asx' => 'video/x-ms-asf',
+ 'atc' => 'application/vnd.acucorp',
+ 'atom' => 'application/atom+xml',
+ 'atomcat' => 'application/atomcat+xml',
+ 'atomsvc' => 'application/atomsvc+xml',
+ 'atx' => 'application/vnd.antix.game-component',
+ 'au' => 'audio/basic',
+ 'avi' => 'video/x-msvideo',
+ 'aw' => 'application/x-applix-word',
+ 'bak' => 'application/x-trash',
+ 'bat' => 'application/x-msdownload',
+ 'bcpio' => 'application/x-bcpio',
+ 'bdf' => 'application/x-font-bdf',
+ 'bdm' => 'application/vnd.syncml.dm+wbxml',
+ 'bh2' => 'application/vnd.fujitsu.oasysprs',
+ 'bib' => 'text/x-bibtex',
+ 'bin' => 'application/octet-stream',
+ 'blend' => 'application/x-blender',
+ 'blender' => 'application/x-blender',
+ 'bmi' => 'application/vnd.bmi',
+ 'bmp' => 'image/bmp',
+ 'box' => 'application/vnd.previewsystems.box',
+ 'boz' => 'application/x-bzip2',
+ 'bpk' => 'application/octet-stream',
+ 'btif' => 'image/prs.btif',
+ 'bz' => 'application/x-bzip',
+ 'bz2' => 'application/x-bzip',
+ 'c' => 'text/x-csrc',
+ 'c++' => 'text/x-c++src',
+ 'c4d' => 'application/vnd.clonk.c4group',
+ 'c4f' => 'application/vnd.clonk.c4group',
+ 'c4g' => 'application/vnd.clonk.c4group',
+ 'c4p' => 'application/vnd.clonk.c4group',
+ 'c4u' => 'application/vnd.clonk.c4group',
+ 'cab' => 'application/vnd.ms-cab-compressed',
+ 'cc' => 'text/x-c++src',
+ 'ccxml' => 'application/ccxml+xml',
+ 'cdbcmsg' => 'application/vnd.contact.cmsg',
+ 'cdf' => 'application/x-netcdf',
+ 'cdkey' => 'application/vnd.mediastation.cdkey',
+ 'cdr' => 'application/vnd.corel-draw',
+ 'cdx' => 'chemical/x-cdx',
+ 'cdxml' => 'application/vnd.chemdraw+xml',
+ 'cdy' => 'application/vnd.cinderella',
+ 'cer' => 'application/x-x509-ca-cert',
+ 'cert' => 'application/x-x509-ca-cert',
+ 'cgi' => 'application/x-cgi',
+ 'cgm' => 'image/cgm',
+ 'chat' => 'application/x-chat',
+ 'chm' => 'application/x-chm',
+ 'chrt' => 'application/x-kchart',
+ 'cif' => 'chemical/x-cif',
+ 'cii' => 'application/vnd.anser-web-certificate-issue-initiation',
+ 'cil' => 'application/vnd.ms-artgalry',
+ 'cla' => 'application/vnd.claymore',
+ 'class' => 'application/x-java',
+ 'clkk' => 'application/vnd.crick.clicker.keyboard',
+ 'clkp' => 'application/vnd.crick.clicker.palette',
+ 'clkt' => 'application/vnd.crick.clicker.template',
+ 'clkw' => 'application/vnd.crick.clicker.wordbank',
+ 'clkx' => 'application/vnd.crick.clicker',
+ 'clp' => 'application/x-msclip',
+ 'cls' => 'text/x-tex',
+ 'cmc' => 'application/vnd.cosmocaller',
+ 'cmdf' => 'chemical/x-cmdf',
+ 'cml' => 'chemical/x-cml',
+ 'cmp' => 'application/vnd.yellowriver-custom-menu',
+ 'cmx' => 'image/x-cmx',
+ 'com' => 'application/x-msdownload',
+ 'conf' => 'text/plain',
+ 'cpio' => 'application/x-cpio',
+ 'cpio.gz' => 'application/x-cpio-compressed',
+ 'cpp' => 'text/x-c++src',
+ 'cpt' => 'application/mac-compactpro',
+ 'crd' => 'application/x-mscardfile',
+ 'crl' => 'application/pkix-crl',
+ 'crt' => 'application/x-x509-ca-cert',
+ 'cs' => 'text/x-csharp',
+ 'csh' => 'application/x-csh',
+ 'csml' => 'chemical/x-csml',
+ 'csp' => 'application/vnd.commonspace',
+ 'css' => 'text/css',
+ 'cst' => 'application/vnd.commonspace',
+ 'csv' => 'text/x-comma-separated-values',
+ 'cur' => 'image/x-win-bitmap',
+ 'curl' => 'application/vnd.curl',
+ 'cww' => 'application/prs.cww',
+ 'cxx' => 'text/x-c++src',
+ 'd' => 'text/x-dsrc',
+ 'daf' => 'application/vnd.mobius.daf',
+ 'dat' => 'video/mpeg',
+ 'davmount' => 'application/davmount+xml',
+ 'dbf' => 'application/x-dbase',
+ 'dc' => 'application/x-dc-rom',
+ 'dcl' => 'text/x-dcl',
+ 'dcm' => 'application/dicom',
+ 'dcr' => 'application/x-director',
+ 'dd2' => 'application/vnd.oma.dd2+xml',
+ 'ddd' => 'application/vnd.fujixerox.ddd',
+ 'deb' => 'application/x-deb',
+ 'def' => 'text/plain',
+ 'der' => 'application/x-x509-ca-cert',
+ 'desktop' => 'application/x-desktop',
+ 'dfac' => 'application/vnd.dreamfactory',
+ 'dia' => 'application/x-dia-diagram',
+ 'dic' => 'text/x-c',
+ 'diff' => 'text/x-patch',
+ 'dir' => 'application/x-director',
+ 'dis' => 'application/vnd.mobius.dis',
+ 'dist' => 'application/octet-stream',
+ 'distz' => 'application/octet-stream',
+ 'djv' => 'image/vnd.djvu',
+ 'djvu' => 'image/vnd.djvu',
+ 'dll' => 'application/x-msdownload',
+ 'dmg' => 'application/octet-stream',
+ 'dms' => 'application/octet-stream',
+ 'dna' => 'application/vnd.dna',
+ 'doc' => 'application/msword',
+ 'dot' => 'application/msword',
+ 'dp' => 'application/vnd.osgi.dp',
+ 'dpg' => 'application/vnd.dpgraph',
+ 'dsc' => 'text/prs.lines.tag',
+ 'dsl' => 'text/x-dsl',
+ 'dtd' => 'text/x-dtd',
+ 'dump' => 'application/octet-stream',
+ 'dvi' => 'application/x-dvi',
+ 'dwf' => 'model/vnd.dwf',
+ 'dwg' => 'image/vnd.dwg',
+ 'dxf' => 'image/vnd.dxf',
+ 'dxp' => 'application/vnd.spotfire.dxp',
+ 'dxr' => 'application/x-director',
+ 'ecelp4800' => 'audio/vnd.nuera.ecelp4800',
+ 'ecelp7470' => 'audio/vnd.nuera.ecelp7470',
+ 'ecelp9600' => 'audio/vnd.nuera.ecelp9600',
+ 'ecma' => 'application/ecmascript',
+ 'edm' => 'application/vnd.novadigm.edm',
+ 'edx' => 'application/vnd.novadigm.edx',
+ 'efif' => 'application/vnd.picsel',
+ 'egon' => 'application/x-egon',
+ 'ei6' => 'application/vnd.pg.osasli',
+ 'el' => 'text/x-emacs-lisp',
+ 'elc' => 'application/octet-stream',
+ 'eml' => 'message/rfc822',
+ 'eol' => 'audio/vnd.digital-winds',
+ 'eot' => 'application/vnd.ms-fontobject',
+ 'eps' => 'image/x-eps',
+ 'epsf' => 'image/x-eps',
+ 'epsi' => 'image/x-eps',
+ 'es3' => 'application/vnd.eszigno3+xml',
+ 'esf' => 'application/vnd.epson.esf',
+ 'et3' => 'application/vnd.eszigno3+xml',
+ 'etheme' => 'application/x-e-theme',
+ 'etx' => 'text/x-setext',
+ 'exe' => 'application/x-ms-dos-executable',
+ 'ext' => 'application/vnd.novadigm.ext',
+ 'ez' => 'application/andrew-inset',
+ 'ez2' => 'application/vnd.ezpix-album',
+ 'ez3' => 'application/vnd.ezpix-package',
+ 'f' => 'text/x-fortran',
+ 'f77' => 'text/x-fortran',
+ 'f90' => 'text/x-fortran',
+ 'fbs' => 'image/vnd.fastbidsheet',
+ 'fdf' => 'application/vnd.fdf',
+ 'fe_launch' => 'application/vnd.denovo.fcselayout-link',
+ 'fg5' => 'application/vnd.fujitsu.oasysgp',
+ 'fgd' => 'application/x-director',
+ 'fig' => 'image/x-xfig',
+ 'fits' => 'image/x-fits',
+ 'flac' => 'audio/x-flac',
+ 'flc' => 'video/x-flic',
+ 'fli' => 'video/x-flic',
+ 'flo' => 'application/vnd.micrografx.flo',
+ 'flw' => 'application/x-kivio',
+ 'flx' => 'text/vnd.fmi.flexstor',
+ 'fly' => 'text/vnd.fly',
+ 'fm' => 'application/vnd.framemaker',
+ 'fnc' => 'application/vnd.frogans.fnc',
+ 'fo' => 'text/x-xslfo',
+ 'for' => 'text/x-fortran',
+ 'fpx' => 'image/vnd.fpx',
+ 'frame' => 'application/vnd.framemaker',
+ 'fsc' => 'application/vnd.fsc.weblaunch',
+ 'fst' => 'image/vnd.fst',
+ 'ftc' => 'application/vnd.fluxtime.clip',
+ 'fti' => 'application/vnd.anser-web-funds-transfer-initiation',
+ 'fvt' => 'video/vnd.fvt',
+ 'fzs' => 'application/vnd.fuzzysheet',
+ 'g3' => 'image/fax-g3',
+ 'gac' => 'application/vnd.groove-account',
+ 'gb' => 'application/x-gameboy-rom',
+ 'gcrd' => 'text/directory',
+ 'gdl' => 'model/vnd.gdl',
+ 'gen' => 'application/x-genesis-rom',
+ 'gg' => 'application/x-sms-rom',
+ 'ghf' => 'application/vnd.groove-help',
+ 'gif' => 'image/gif',
+ 'gim' => 'application/vnd.groove-identity-message',
+ 'glade' => 'application/x-glade',
+ 'gmo' => 'application/x-gettext-translation',
+ 'gnc' => 'application/x-gnucash',
+ 'gnucash' => 'application/x-gnucash',
+ 'gnumeric' => 'application/x-gnumeric',
+ 'gph' => 'application/vnd.flographit',
+ 'gqf' => 'application/vnd.grafeq',
+ 'gqs' => 'application/vnd.grafeq',
+ 'gra' => 'application/x-graphite',
+ 'gram' => 'application/srgs',
+ 'grv' => 'application/vnd.groove-injector',
+ 'grxml' => 'application/srgs+xml',
+ 'gsf' => 'application/x-font-type1',
+ 'gsm' => 'audio/x-gsm',
+ 'gtar' => 'application/x-gtar',
+ 'gtm' => 'application/vnd.groove-tool-message',
+ 'gtw' => 'model/vnd.gtw',
+ 'gz' => 'application/x-gzip',
+ 'h' => 'text/x-chdr',
+ 'h++' => 'text/x-chdr',
+ 'h261' => 'video/h261',
+ 'h263' => 'video/h263',
+ 'h264' => 'video/h264',
+ 'hbci' => 'application/vnd.hbci',
+ 'hdf' => 'application/x-hdf',
+ 'hh' => 'text/x-c++hdr',
+ 'hlp' => 'application/winhlp',
+ 'hp' => 'text/x-chdr',
+ 'hpgl' => 'application/vnd.hp-hpgl',
+ 'hpid' => 'application/vnd.hp-hpid',
+ 'hps' => 'application/vnd.hp-hps',
+ 'hqx' => 'application/mac-binhex40',
+ 'hs' => 'text/x-haskell',
+ 'htke' => 'application/vnd.kenameaapp',
+ 'htm' => 'text/html',
+ 'html' => 'text/html',
+ 'hvd' => 'application/vnd.yamaha.hv-dic',
+ 'hvp' => 'application/vnd.yamaha.hv-voice',
+ 'hvs' => 'application/vnd.yamaha.hv-script',
+ 'icb' => 'image/x-icb',
+ 'ice' => 'x-conference/x-cooltalk',
+ 'ico' => 'image/x-ico',
+ 'ics' => 'text/calendar',
+ 'idl' => 'text/x-idl',
+ 'ief' => 'image/ief',
+ 'ifb' => 'text/calendar',
+ 'iff' => 'image/x-iff',
+ 'ifm' => 'application/vnd.shana.informed.formdata',
+ 'iges' => 'model/iges',
+ 'igl' => 'application/vnd.igloader',
+ 'igs' => 'model/iges',
+ 'igx' => 'application/vnd.micrografx.igx',
+ 'iif' => 'application/vnd.shana.informed.interchange',
+ 'ilbm' => 'image/x-ilbm',
+ 'imp' => 'application/vnd.accpac.simply.imp',
+ 'ims' => 'application/vnd.ms-ims',
+ 'in' => 'text/plain',
+ 'ipk' => 'application/vnd.shana.informed.package',
+ 'irm' => 'application/vnd.ibm.rights-management',
+ 'irp' => 'application/vnd.irepository.package+xml',
+ 'iso' => 'application/x-cd-image',
+ 'it' => 'audio/x-it',
+ 'itp' => 'application/vnd.shana.informed.formtemplate',
+ 'ivp' => 'application/vnd.immervision-ivp',
+ 'ivu' => 'application/vnd.immervision-ivu',
+ 'jad' => 'text/vnd.sun.j2me.app-descriptor',
+ 'jam' => 'application/vnd.jam',
+ 'jar' => 'application/x-jar',
+ 'java' => 'text/x-java',
+ 'jisp' => 'application/vnd.jisp',
+ 'jlt' => 'application/vnd.hp-jlyt',
+ 'jng' => 'image/x-jng',
+ 'jnlp' => 'application/x-java-jnlp-file',
+ 'joda' => 'application/vnd.joost.joda-archive',
+ 'jp2' => 'image/jpeg2000',
+ 'jpe' => 'image/jpeg',
+ 'jpeg' => 'image/jpeg',
+ 'jpg' => 'image/jpeg',
+ 'jpgm' => 'video/jpm',
+ 'jpgv' => 'video/jpeg',
+ 'jpm' => 'video/jpm',
+ 'jpr' => 'application/x-jbuilder-project',
+ 'jpx' => 'application/x-jbuilder-project',
+ 'js' => 'application/x-javascript',
+ 'json' => 'application/json',
+ 'kar' => 'audio/midi',
+ 'karbon' => 'application/x-karbon',
+ 'kdelnk' => 'application/x-desktop',
+ 'kfo' => 'application/x-kformula',
+ 'kia' => 'application/vnd.kidspiration',
+ 'kil' => 'application/x-killustrator',
+ 'kml' => 'application/vnd.google-earth.kml+xml',
+ 'kmz' => 'application/vnd.google-earth.kmz',
+ 'kne' => 'application/vnd.kinar',
+ 'knp' => 'application/vnd.kinar',
+ 'kon' => 'application/x-kontour',
+ 'kpm' => 'application/x-kpovmodeler',
+ 'kpr' => 'application/x-kpresenter',
+ 'kpt' => 'application/x-kpresenter',
+ 'kra' => 'application/x-krita',
+ 'ksp' => 'application/x-kspread',
+ 'ktr' => 'application/vnd.kahootz',
+ 'ktz' => 'application/vnd.kahootz',
+ 'kud' => 'application/x-kugar',
+ 'kwd' => 'application/x-kword',
+ 'kwt' => 'application/x-kword',
+ 'la' => 'application/x-shared-library-la',
+ 'latex' => 'application/x-latex',
+ 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop',
+ 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml',
+ 'les' => 'application/vnd.hhe.lesson-player',
+ 'lha' => 'application/x-lha',
+ 'lhs' => 'text/x-literate-haskell',
+ 'lhz' => 'application/x-lhz',
+ 'list' => 'text/plain',
+ 'list3820' => 'application/vnd.ibm.modcap',
+ 'listafp' => 'application/vnd.ibm.modcap',
+ 'log' => 'text/x-log',
+ 'lrm' => 'application/vnd.ms-lrm',
+ 'ltf' => 'application/vnd.frogans.ltf',
+ 'ltx' => 'text/x-tex',
+ 'lvp' => 'audio/vnd.lucent.voice',
+ 'lwo' => 'image/x-lwo',
+ 'lwob' => 'image/x-lwo',
+ 'lwp' => 'application/vnd.lotus-wordpro',
+ 'lws' => 'image/x-lws',
+ 'lyx' => 'application/x-lyx',
+ 'lzh' => 'application/x-lha',
+ 'lzo' => 'application/x-lzop',
+ 'm' => 'text/x-objcsrc',
+ 'm13' => 'application/x-msmediaview',
+ 'm14' => 'application/x-msmediaview',
+ 'm15' => 'audio/x-mod',
+ 'm1v' => 'video/mpeg',
+ 'm2a' => 'audio/mpeg',
+ 'm2v' => 'video/mpeg',
+ 'm3a' => 'audio/mpeg',
+ 'm3u' => 'audio/x-mpegurl',
+ 'm4a' => 'audio/x-m4a',
+ 'm4u' => 'video/vnd.mpegurl',
+ 'ma' => 'application/mathematica',
+ 'mag' => 'application/vnd.ecowin.chart',
+ 'maker' => 'application/vnd.framemaker',
+ 'man' => 'application/x-troff-man',
+ 'mathml' => 'application/mathml+xml',
+ 'mb' => 'application/mathematica',
+ 'mbk' => 'application/vnd.mobius.mbk',
+ 'mbox' => 'application/mbox',
+ 'mc1' => 'application/vnd.medcalcdata',
+ 'mcd' => 'application/vnd.mcd',
+ 'md' => 'application/x-genesis-rom',
+ 'mdb' => 'application/x-msaccess',
+ 'mdi' => 'image/vnd.ms-modi',
+ 'me' => 'text/x-troff-me',
+ 'mesh' => 'model/mesh',
+ 'mfm' => 'application/vnd.mfmp',
+ 'mgp' => 'application/x-magicpoint',
+ 'mgz' => 'application/vnd.proteus.magazine',
+ 'mid' => 'audio/midi',
+ 'midi' => 'audio/midi',
+ 'mif' => 'application/x-mif',
+ 'mime' => 'message/rfc822',
+ 'mj2' => 'video/mj2',
+ 'mjp2' => 'video/mj2',
+ 'mkv' => 'application/x-matroska',
+ 'mlp' => 'application/vnd.dolby.mlp',
+ 'mm' => 'text/x-troff-mm',
+ 'mmd' => 'application/vnd.chipnuts.karaoke-mmd',
+ 'mmf' => 'application/vnd.smaf',
+ 'mml' => 'text/mathml',
+ 'mmr' => 'image/vnd.fujixerox.edmics-mmr',
+ 'mng' => 'video/x-mng',
+ 'mny' => 'application/x-msmoney',
+ 'moc' => 'text/x-moc',
+ 'mod' => 'audio/x-mod',
+ 'moov' => 'video/quicktime',
+ 'mov' => 'video/quicktime',
+ 'movie' => 'video/x-sgi-movie',
+ 'mp2' => 'video/mpeg',
+ 'mp2a' => 'audio/mpeg',
+ 'mp3' => 'audio/mpeg',
+ 'mp4' => 'video/mp4',
+ 'mp4a' => 'audio/mp4',
+ 'mp4s' => 'application/mp4',
+ 'mp4v' => 'video/mp4',
+ 'mpc' => 'application/vnd.mophun.certificate',
+ 'mpe' => 'video/mpeg',
+ 'mpeg' => 'video/mpeg',
+ 'mpg' => 'video/mpeg',
+ 'mpg4' => 'video/mp4',
+ 'mpga' => 'audio/mpeg',
+ 'mpkg' => 'application/vnd.apple.installer+xml',
+ 'mpm' => 'application/vnd.blueice.multipass',
+ 'mpn' => 'application/vnd.mophun.application',
+ 'mpp' => 'application/vnd.ms-project',
+ 'mpt' => 'application/vnd.ms-project',
+ 'mpy' => 'application/vnd.ibm.minipay',
+ 'mqy' => 'application/vnd.mobius.mqy',
+ 'mrc' => 'application/marc',
+ 'ms' => 'text/x-troff-ms',
+ 'mscml' => 'application/mediaservercontrol+xml',
+ 'mseq' => 'application/vnd.mseq',
+ 'msf' => 'application/vnd.epson.msf',
+ 'msh' => 'model/mesh',
+ 'msi' => 'application/x-msdownload',
+ 'msl' => 'application/vnd.mobius.msl',
+ 'msod' => 'image/x-msod',
+ 'msty' => 'application/vnd.muvee.style',
+ 'msx' => 'application/x-msx-rom',
+ 'mtm' => 'audio/x-mod',
+ 'mts' => 'model/vnd.mts',
+ 'mus' => 'application/vnd.musician',
+ 'mvb' => 'application/x-msmediaview',
+ 'mwf' => 'application/vnd.mfer',
+ 'mxf' => 'application/mxf',
+ 'mxl' => 'application/vnd.recordare.musicxml',
+ 'mxml' => 'application/xv+xml',
+ 'mxs' => 'application/vnd.triscape.mxs',
+ 'mxu' => 'video/vnd.mpegurl',
+ 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install',
+ 'n64' => 'application/x-n64-rom',
+ 'nb' => 'application/mathematica',
+ 'nc' => 'application/x-netcdf',
+ 'nes' => 'application/x-nes-rom',
+ 'ngdat' => 'application/vnd.nokia.n-gage.data',
+ 'nlu' => 'application/vnd.neurolanguage.nlu',
+ 'nml' => 'application/vnd.enliven',
+ 'nnd' => 'application/vnd.noblenet-directory',
+ 'nns' => 'application/vnd.noblenet-sealer',
+ 'nnw' => 'application/vnd.noblenet-web',
+ 'npx' => 'image/vnd.net-fpx',
+ 'nsf' => 'application/vnd.lotus-notes',
+ 'nsv' => 'video/x-nsv',
+ 'o' => 'application/x-object',
+ 'oa2' => 'application/vnd.fujitsu.oasys2',
+ 'oa3' => 'application/vnd.fujitsu.oasys3',
+ 'oas' => 'application/vnd.fujitsu.oasys',
+ 'obd' => 'application/x-msbinder',
+ 'obj' => 'application/x-tgif',
+ 'oda' => 'application/oda',
+ 'odb' => 'application/vnd.oasis.opendocument.database',
+ 'odc' => 'application/vnd.oasis.opendocument.chart',
+ 'odf' => 'application/vnd.oasis.opendocument.formula',
+ 'odg' => 'application/vnd.oasis.opendocument.graphics',
+ 'odi' => 'application/vnd.oasis.opendocument.image',
+ 'odm' => 'application/vnd.oasis.opendocument.text-master',
+ 'odp' => 'application/vnd.oasis.opendocument.presentation',
+ 'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
+ 'odt' => 'application/vnd.oasis.opendocument.text',
+ 'ogg' => 'application/ogg',
+ 'old' => 'application/x-trash',
+ 'oleo' => 'application/x-oleo',
+ 'oot' => 'application/vnd.oasis.opendocument.text',
+ 'oprc' => 'application/vnd.palm',
+ 'org' => 'application/vnd.lotus-organizer',
+ 'otc' => 'application/vnd.oasis.opendocument.chart-template',
+ 'otf' => 'application/vnd.oasis.opendocument.formula-template',
+ 'otg' => 'application/vnd.oasis.opendocument.graphics-template',
+ 'oth' => 'application/vnd.oasis.opendocument.text-web',
+ 'oti' => 'application/vnd.oasis.opendocument.image-template',
+ 'otm' => 'application/vnd.oasis.opendocument.text-master',
+ 'otp' => 'application/vnd.oasis.opendocument.presentation-template',
+ 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 'ott' => 'application/vnd.oasis.opendocument.text-template',
+ 'oxt' => 'application/vnd.openofficeorg.extension',
+ 'p' => 'text/x-pascal',
+ 'p10' => 'application/pkcs10',
+ 'p12' => 'application/x-pkcs12',
+ 'p7b' => 'application/x-pkcs7-certificates',
+ 'p7c' => 'application/pkcs7-mime',
+ 'p7m' => 'application/pkcs7-mime',
+ 'p7r' => 'application/x-pkcs7-certreqresp',
+ 'p7s' => 'application/pkcs7-signature',
+ 'par2' => 'application/x-par2',
+ 'pas' => 'text/x-pascal',
+ 'patch' => 'text/x-patch',
+ 'pbd' => 'application/vnd.powerbuilder6',
+ 'pbm' => 'image/x-portable-bitmap',
+ 'pcd' => 'image/x-photo-cd',
+ 'pcf' => 'application/x-font-pcf',
+ 'pcf.Z' => 'application/x-font-type1',
+ 'pcl' => 'application/vnd.hp-pcl',
+ 'pclxl' => 'application/vnd.hp-pclxl',
+ 'pct' => 'image/x-pict',
+ 'pcx' => 'image/x-pcx',
+ 'pdb' => 'application/vnd.palm',
+ 'pdf' => 'application/pdf',
+ 'pem' => 'application/x-x509-ca-cert',
+ 'perl' => 'application/x-perl',
+ 'pfa' => 'application/x-font-type1',
+ 'pfb' => 'application/x-font-type1',
+ 'pfr' => 'application/font-tdpfr',
+ 'pfx' => 'application/x-pkcs12',
+ 'pgm' => 'image/x-portable-graymap',
+ 'pgn' => 'application/x-chess-pgn',
+ 'pgp' => 'application/pgp',
+ 'php' => 'application/x-php',
+ 'php3' => 'application/x-php',
+ 'php4' => 'application/x-php',
+ 'pic' => 'image/x-pict',
+ 'pict' => 'image/x-pict',
+ 'pict1' => 'image/x-pict',
+ 'pict2' => 'image/x-pict',
+ 'pkg' => 'application/octet-stream',
+ 'pki' => 'application/pkixcmp',
+ 'pkipath' => 'application/pkix-pkipath',
+ 'pl' => 'application/x-perl',
+ 'plb' => 'application/vnd.3gpp.pic-bw-large',
+ 'plc' => 'application/vnd.mobius.plc',
+ 'plf' => 'application/vnd.pocketlearn',
+ 'pls' => 'audio/x-scpls',
+ 'pm' => 'application/x-perl',
+ 'pml' => 'application/vnd.ctc-posml',
+ 'png' => 'image/png',
+ 'pnm' => 'image/x-portable-anymap',
+ 'po' => 'text/x-gettext-translation',
+ 'portpkg' => 'application/vnd.macports.portpkg',
+ 'pot' => 'text/x-gettext-translation-template',
+ 'ppd' => 'application/vnd.cups-ppd',
+ 'ppm' => 'image/x-portable-pixmap',
+ 'pps' => 'application/vnd.ms-powerpoint',
+ 'ppt' => 'application/vnd.ms-powerpoint',
+ 'ppz' => 'application/vnd.ms-powerpoint',
+ 'pqa' => 'application/vnd.palm',
+ 'prc' => 'application/vnd.palm',
+ 'pre' => 'application/vnd.lotus-freelance',
+ 'prf' => 'application/pics-rules',
+ 'ps' => 'application/postscript',
+ 'ps.gz' => 'application/x-gzpostscript',
+ 'psb' => 'application/vnd.3gpp.pic-bw-small',
+ 'psd' => 'image/x-psd',
+ 'psf' => 'application/x-font-linux-psf',
+ 'psid' => 'audio/prs.sid',
+ 'ptid' => 'application/vnd.pvi.ptid1',
+ 'pub' => 'application/x-mspublisher',
+ 'pvb' => 'application/vnd.3gpp.pic-bw-var',
+ 'pw' => 'application/x-pw',
+ 'pwn' => 'application/vnd.3m.post-it-notes',
+ 'py' => 'text/x-python',
+ 'pyc' => 'application/x-python-bytecode',
+ 'pyo' => 'application/x-python-bytecode',
+ 'qam' => 'application/vnd.epson.quickanime',
+ 'qbo' => 'application/vnd.intu.qbo',
+ 'qfx' => 'application/vnd.intu.qfx',
+ 'qif' => 'application/x-qw',
+ 'qps' => 'application/vnd.publishare-delta-tree',
+ 'qt' => 'video/quicktime',
+ 'qtvr' => 'video/quicktime',
+ 'qwd' => 'application/vnd.quark.quarkxpress',
+ 'qwt' => 'application/vnd.quark.quarkxpress',
+ 'qxb' => 'application/vnd.quark.quarkxpress',
+ 'qxd' => 'application/vnd.quark.quarkxpress',
+ 'qxl' => 'application/vnd.quark.quarkxpress',
+ 'qxt' => 'application/vnd.quark.quarkxpress',
+ 'ra' => 'audio/x-pn-realaudio',
+ 'ram' => 'audio/x-pn-realaudio',
+ 'rar' => 'application/x-rar',
+ 'ras' => 'image/x-cmu-raster',
+ 'rcprofile' => 'application/vnd.ipunplugged.rcprofile',
+ 'rdf' => 'text/rdf',
+ 'rdz' => 'application/vnd.data-vision.rdz',
+ 'rej' => 'application/x-reject',
+ 'rep' => 'application/vnd.businessobjects',
+ 'rgb' => 'image/x-rgb',
+ 'rif' => 'application/reginfo+xml',
+ 'rl' => 'application/resource-lists+xml',
+ 'rlc' => 'image/vnd.fujixerox.edmics-rlc',
+ 'rle' => 'image/rle',
+ 'rm' => 'audio/x-pn-realaudio',
+ 'rmi' => 'audio/midi',
+ 'rmp' => 'audio/x-pn-realaudio-plugin',
+ 'rms' => 'application/vnd.jcp.javame.midlet-rms',
+ 'rnc' => 'application/relax-ng-compact-syntax',
+ 'roff' => 'application/x-troff',
+ 'rpm' => 'application/x-rpm',
+ 'rpss' => 'application/vnd.nokia.radio-presets',
+ 'rpst' => 'application/vnd.nokia.radio-preset',
+ 'rq' => 'application/sparql-query',
+ 'rs' => 'application/rls-services+xml',
+ 'rsd' => 'application/rsd+xml',
+ 'rss' => 'text/rss',
+ 'rtf' => 'application/rtf',
+ 'rtx' => 'text/richtext',
+ 's' => 'text/x-asm',
+ 's3m' => 'audio/x-s3m',
+ 'saf' => 'application/vnd.yamaha.smaf-audio',
+ 'sam' => 'application/x-amipro',
+ 'sbml' => 'application/sbml+xml',
+ 'sc' => 'application/vnd.ibm.secure-container',
+ 'scd' => 'application/x-msschedule',
+ 'scm' => 'text/x-scheme',
+ 'scq' => 'application/scvp-cv-request',
+ 'scs' => 'application/scvp-cv-response',
+ 'sda' => 'application/vnd.stardivision.draw',
+ 'sdc' => 'application/vnd.stardivision.calc',
+ 'sdd' => 'application/vnd.stardivision.impress',
+ 'sdkd' => 'application/vnd.solent.sdkm+xml',
+ 'sdkm' => 'application/vnd.solent.sdkm+xml',
+ 'sdp' => 'application/vnd.stardivision.impress',
+ 'sds' => 'application/vnd.stardivision.chart',
+ 'sdw' => 'application/vnd.stardivision.writer',
+ 'see' => 'application/vnd.seemail',
+ 'sema' => 'application/vnd.sema',
+ 'semd' => 'application/vnd.semd',
+ 'semf' => 'application/vnd.semf',
+ 'setpay' => 'application/set-payment-initiation',
+ 'setreg' => 'application/set-registration-initiation',
+ 'sfs' => 'application/vnd.spotfire.sfs',
+ 'sgi' => 'image/x-sgi',
+ 'sgl' => 'application/vnd.stardivision.writer',
+ 'sgm' => 'text/sgml',
+ 'sgml' => 'text/sgml',
+ 'sh' => 'application/x-shellscript',
+ 'shar' => 'application/x-shar',
+ 'shf' => 'application/shf+xml',
+ 'shtml' => 'text/html',
+ 'siag' => 'application/x-siag',
+ 'sid' => 'audio/prs.sid',
+ 'sig' => 'application/pgp-signature',
+ 'sik' => 'application/x-trash',
+ 'silo' => 'model/mesh',
+ 'sit' => 'application/stuffit',
+ 'sitx' => 'application/x-stuffitx',
+ 'skd' => 'application/vnd.koan',
+ 'skm' => 'application/vnd.koan',
+ 'skp' => 'application/vnd.koan',
+ 'skt' => 'application/vnd.koan',
+ 'slk' => 'text/spreadsheet',
+ 'slt' => 'application/vnd.epson.salt',
+ 'smd' => 'application/vnd.stardivision.mail',
+ 'smf' => 'application/vnd.stardivision.math',
+ 'smi' => 'application/smil',
+ 'smil' => 'application/smil',
+ 'sml' => 'application/smil',
+ 'sms' => 'application/x-sms-rom',
+ 'snd' => 'audio/basic',
+ 'so' => 'application/x-sharedlib',
+ 'spc' => 'application/x-pkcs7-certificates',
+ 'spd' => 'application/x-font-speedo',
+ 'spf' => 'application/vnd.yamaha.smaf-phrase',
+ 'spl' => 'application/x-futuresplash',
+ 'spot' => 'text/vnd.in3d.spot',
+ 'spp' => 'application/scvp-vp-response',
+ 'spq' => 'application/scvp-vp-request',
+ 'sql' => 'text/x-sql',
+ 'src' => 'application/x-wais-source',
+ 'srx' => 'application/sparql-results+xml',
+ 'ssf' => 'application/vnd.epson.ssf',
+ 'ssml' => 'application/ssml+xml',
+ 'stc' => 'application/vnd.sun.xml.calc.template',
+ 'std' => 'application/vnd.sun.xml.draw.template',
+ 'stf' => 'application/vnd.wt.stf',
+ 'sti' => 'application/vnd.sun.xml.impress.template',
+ 'stk' => 'application/hyperstudio',
+ 'stm' => 'audio/x-stm',
+ 'str' => 'application/vnd.pg.format',
+ 'stw' => 'application/vnd.sun.xml.writer.template',
+ 'sty' => 'text/x-tex',
+ 'sun' => 'image/x-sun-raster',
+ 'sus' => 'application/vnd.sus-calendar',
+ 'susp' => 'application/vnd.sus-calendar',
+ 'sv4cpio' => 'application/x-sv4cpio',
+ 'sv4crc' => 'application/x-sv4crc',
+ 'svd' => 'application/vnd.svd',
+ 'svg' => 'image/svg+xml',
+ 'svgz' => 'image/svg+xml',
+ 'swf' => 'application/x-shockwave-flash',
+ 'sxc' => 'application/vnd.sun.xml.calc',
+ 'sxd' => 'application/vnd.sun.xml.draw',
+ 'sxg' => 'application/vnd.sun.xml.writer.global',
+ 'sxi' => 'application/vnd.sun.xml.impress',
+ 'sxm' => 'application/vnd.sun.xml.math',
+ 'sxw' => 'application/vnd.sun.xml.writer',
+ 'sylk' => 'text/spreadsheet',
+ 't' => 'application/x-troff',
+ 'tao' => 'application/vnd.tao.intent-module-archive',
+ 'tar' => 'application/x-tar',
+ 'tar.Z' => 'application/x-tarz',
+ 'tar.bz' => 'application/x-bzip-compressed-tar',
+ 'tar.bz2' => 'application/x-bzip-compressed-tar',
+ 'tar.gz' => 'application/x-compressed-tar',
+ 'tar.lzo' => 'application/x-tzo',
+ 'tcap' => 'application/vnd.3gpp2.tcap',
+ 'tcl' => 'text/x-tcl',
+ 'tex' => 'text/x-tex',
+ 'texi' => 'text/x-texinfo',
+ 'texinfo' => 'text/x-texinfo',
+ 'text' => 'text/plain',
+ 'tga' => 'image/x-tga',
+ 'tgz' => 'application/x-compressed-tar',
+ 'theme' => 'application/x-theme',
+ 'tif' => 'image/tiff',
+ 'tiff' => 'image/tiff',
+ 'tk' => 'text/x-tcl',
+ 'tmo' => 'application/vnd.tmobile-livetv',
+ 'torrent' => 'application/x-bittorrent',
+ 'tpl' => 'application/vnd.groove-tool-template',
+ 'tpt' => 'application/vnd.trid.tpt',
+ 'tr' => 'application/x-troff',
+ 'tra' => 'application/vnd.trueapp',
+ 'trm' => 'application/x-msterminal',
+ 'ts' => 'application/x-linguist',
+ 'tsv' => 'text/tab-separated-values',
+ 'ttf' => 'application/x-font-ttf',
+ 'twd' => 'application/vnd.simtech-mindmapper',
+ 'twds' => 'application/vnd.simtech-mindmapper',
+ 'txd' => 'application/vnd.genomatix.tuxedo',
+ 'txf' => 'application/vnd.mobius.txf',
+ 'txt' => 'text/plain',
+ 'tzo' => 'application/x-tzo',
+ 'ufd' => 'application/vnd.ufdl',
+ 'ufdl' => 'application/vnd.ufdl',
+ 'ui' => 'application/x-designer',
+ 'uil' => 'text/x-uil',
+ 'ult' => 'audio/x-mod',
+ 'umj' => 'application/vnd.umajin',
+ 'uni' => 'audio/x-mod',
+ 'unityweb' => 'application/vnd.unity',
+ 'uoml' => 'application/vnd.uoml+xml',
+ 'uri' => 'text/x-uri',
+ 'uris' => 'text/uri-list',
+ 'url' => 'text/x-uri',
+ 'urls' => 'text/uri-list',
+ 'ustar' => 'application/x-ustar',
+ 'utz' => 'application/vnd.uiq.theme',
+ 'uu' => 'text/x-uuencode',
+ 'vcd' => 'application/x-cdlink',
+ 'vcf' => 'text/directory',
+ 'vcg' => 'application/vnd.groove-vcard',
+ 'vcs' => 'text/calendar',
+ 'vct' => 'text/directory',
+ 'vcx' => 'application/vnd.vcx',
+ 'vfb' => 'text/calendar',
+ 'vis' => 'application/vnd.visionary',
+ 'viv' => 'video/vnd.vivo',
+ 'vob' => 'video/mpeg',
+ 'voc' => 'audio/x-voc',
+ 'vor' => 'application/vnd.stardivision.writer',
+ 'vrml' => 'model/vrml',
+ 'vsd' => 'application/vnd.visio',
+ 'vsf' => 'application/vnd.vsf',
+ 'vss' => 'application/vnd.visio',
+ 'vst' => 'application/vnd.visio',
+ 'vsw' => 'application/vnd.visio',
+ 'vtu' => 'model/vnd.vtu',
+ 'vxml' => 'application/voicexml+xml',
+ 'wav' => 'audio/x-wav',
+ 'wax' => 'audio/x-ms-wax',
+ 'wb1' => 'application/x-quattropro',
+ 'wb2' => 'application/x-quattropro',
+ 'wb3' => 'application/x-quattropro',
+ 'wbmp' => 'image/vnd.wap.wbmp',
+ 'wbs' => 'application/vnd.criticaltools.wbs+xml',
+ 'wbxml' => 'application/vnd.wap.wbxml',
+ 'wcm' => 'application/vnd.ms-works',
+ 'wdb' => 'application/vnd.ms-works',
+ 'wk1' => 'application/vnd.lotus-1-2-3',
+ 'wk3' => 'application/vnd.lotus-1-2-3',
+ 'wk4' => 'application/vnd.lotus-1-2-3',
+ 'wks' => 'application/vnd.lotus-1-2-3',
+ 'wm' => 'video/x-ms-wm',
+ 'wma' => 'audio/x-ms-wma',
+ 'wmd' => 'application/x-ms-wmd',
+ 'wmf' => 'image/x-wmf',
+ 'wml' => 'text/vnd.wap.wml',
+ 'wmlc' => 'application/vnd.wap.wmlc',
+ 'wmls' => 'text/vnd.wap.wmlscript',
+ 'wmlsc' => 'application/vnd.wap.wmlscriptc',
+ 'wmv' => 'video/x-ms-wmv',
+ 'wmx' => 'video/x-ms-wmx',
+ 'wmz' => 'application/x-ms-wmz',
+ 'wpd' => 'application/vnd.wordperfect',
+ 'wpg' => 'application/x-wpg',
+ 'wpl' => 'application/vnd.ms-wpl',
+ 'wps' => 'application/vnd.ms-works',
+ 'wqd' => 'application/vnd.wqd',
+ 'wri' => 'application/x-mswrite',
+ 'wrl' => 'model/vrml',
+ 'wsdl' => 'application/wsdl+xml',
+ 'wspolicy' => 'application/wspolicy+xml',
+ 'wtb' => 'application/vnd.webturbo',
+ 'wvx' => 'video/x-ms-wvx',
+ 'x3d' => 'application/vnd.hzn-3d-crossword',
+ 'xac' => 'application/x-gnucash',
+ 'xar' => 'application/vnd.xara',
+ 'xbd' => 'application/vnd.fujixerox.docuworks.binder',
+ 'xbel' => 'application/x-xbel',
+ 'xbm' => 'image/x-xbitmap',
+ 'xcf' => 'image/x-xcf',
+ 'xcf.bz2' => 'image/x-compressed-xcf',
+ 'xcf.gz' => 'image/x-compressed-xcf',
+ 'xdm' => 'application/vnd.syncml.dm+xml',
+ 'xdp' => 'application/vnd.adobe.xdp+xml',
+ 'xdw' => 'application/vnd.fujixerox.docuworks',
+ 'xenc' => 'application/xenc+xml',
+ 'xfdf' => 'application/vnd.adobe.xfdf',
+ 'xfdl' => 'application/vnd.xfdl',
+ 'xht' => 'application/xhtml+xml',
+ 'xhtml' => 'application/xhtml+xml',
+ 'xhvml' => 'application/xv+xml',
+ 'xi' => 'audio/x-xi',
+ 'xif' => 'image/vnd.xiff',
+ 'xla' => 'application/vnd.ms-excel',
+ 'xlc' => 'application/vnd.ms-excel',
+ 'xld' => 'application/vnd.ms-excel',
+ 'xll' => 'application/vnd.ms-excel',
+ 'xlm' => 'application/vnd.ms-excel',
+ 'xls' => 'application/vnd.ms-excel',
+ 'xlt' => 'application/vnd.ms-excel',
+ 'xlw' => 'application/vnd.ms-excel',
+ 'xm' => 'audio/x-xm',
+ 'xmi' => 'text/x-xmi',
+ 'xml' => 'text/xml',
+ 'xo' => 'application/vnd.olpc-sugar',
+ 'xop' => 'application/xop+xml',
+ 'xpm' => 'image/x-xpixmap',
+ 'xpr' => 'application/vnd.is-xpr',
+ 'xps' => 'application/vnd.ms-xpsdocument',
+ 'xpw' => 'application/vnd.intercon.formnet',
+ 'xpx' => 'application/vnd.intercon.formnet',
+ 'xsl' => 'text/x-xslt',
+ 'xslfo' => 'text/x-xslfo',
+ 'xslt' => 'text/x-xslt',
+ 'xsm' => 'application/vnd.syncml+xml',
+ 'xspf' => 'application/xspf+xml',
+ 'xul' => 'application/vnd.mozilla.xul+xml',
+ 'xvm' => 'application/xv+xml',
+ 'xvml' => 'application/xv+xml',
+ 'xwd' => 'image/x-xwindowdump',
+ 'xyz' => 'chemical/x-xyz',
+ 'zabw' => 'application/x-abiword',
+ 'zaz' => 'application/vnd.zzazz.deck+xml',
+ 'zip' => 'application/zip',
+ 'zmm' => 'application/vnd.handheld-entertainment+xml',
+ 'zoo' => 'application/x-zoo',
+ '123' => 'application/vnd.lotus-1-2-3',
+ '669' => 'audio/x-mod'
+);
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<package packagerversion="1.4.9" version="2.0" xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+http://pear.php.net/dtd/tasks-1.0.xsd
+http://pear.php.net/dtd/package-2.0
+http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Horde_MIME</name>
+ <channel>pear.horde.org</channel>
+ <summary>Horde Mime Library</summary>
+ <description>The Horde_MIME:: class provides methods for dealing with MIME (RFC 2045) and related e-mail (RFC 822/2822/5322) standards.
+ </description>
+ <lead>
+ <name>Chuck Hagenbuch</name>
+ <user>chuck</user>
+ <email>chuck@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <lead>
+ <name>Michael Slusarz</name>
+ <user>slusarz</user>
+ <email>slusarz@horde.org</email>
+ <active>yes</active>
+ </lead>
+ <date>2008-10-29</date>
+ <time>14:00:00</time>
+ <version>
+ <release>0.1.0</release>
+ <api>0.1.0</api>
+ </version>
+ <stability>
+ <release>alpha</release>
+ <api>alpha</api>
+ </stability>
+ <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
+ <notes>* Initial package.</notes>
+ <contents>
+ <dir name="/">
+ <dir name="lib">
+ <dir name="Horde">
+ <dir name="MIME">
+ <dir name="Viewer">
+ <dir name="ooo">
+ <file name="common.xsl" role="php" />
+ <file name="global_document.xsl" role="php" />
+ <file name="main_html.xsl" role="php" />
+ <file name="palm.xsl" role="php" />
+ <file name="style_header.xsl" role="php" />
+ <file name="style_inlined.xsl" role="php" />
+ <file name="style_mapping.xsl" role="php" />
+ <file name="table.xsl" role="php" />
+ <file name="table_cells.xsl" role="php" />
+ <file name="table_columns.xsl" role="php" />
+ <file name="table_rows.xsl" role="php" />
+ </dir> <!-- /lib/Horde/MIME/Viewer/ooo -->
+ <file name="Driver.php" role="php" />
+ <file name="audio.php" role="php" />
+ <file name="css.php" role="php" />
+ <file name="deb.php" role="php" />
+ <file name="default.php" role="php" />
+ <file name="enriched.php" role="php" />
+ <file name="enscript.php" role="php" />
+ <file name="html.php" role="php" />
+ <file name="images.php" role="php" />
+ <file name="msexcel.php" role="php" />
+ <file name="mspowerpoint.php" role="php" />
+ <file name="msword.php" role="php" />
+ <file name="ooo.php" role="php" />
+ <file name="pdf.php" role="php" />
+ <file name="php.php" role="php" />
+ <file name="plain.php" role="php" />
+ <file name="rar.php" role="php" />
+ <file name="report.php" role="php" />
+ <file name="rfc822.php" role="php" />
+ <file name="richtext.php" role="php" />
+ <file name="rpm.php" role="php" />
+ <file name="rtf.php" role="php" />
+ <file name="security.php" role="php" />
+ <file name="simple.php" role="php" />
+ <file name="smil.php" role="php" />
+ <file name="source.php" role="php" />
+ <file name="srchighlite.php" role="php" />
+ <file name="tgz.php" role="php" />
+ <file name="tnef.php" role="php" />
+ <file name="vcard.php" role="php" />
+ <file name="webcpp.php" role="php" />
+ <file name="wordperfect.php" role="php" />
+ <file name="zip.php" role="php" />
+ </dir> <!-- /lib/Horde/MIME/Viewer -->
+ <file name="Address.php" role="php" />
+ <file name="Headers.php" role="php" />
+ <file name="Magic.php" role="php" />
+ <file name="Mail.php" role="php" />
+ <file name="MDN.php" role="php" />
+ <file name="Message.php" role="php" />
+ <file name="Part.php" role="php" />
+ <file name="Viewer.php" role="php" />
+ <file name="mime.magic.php" role="php" />
+ <file name="mime.mapping.php" role="php" />
+ </dir> <!-- /lib/Horde/MIME -->
+ <file name="MIME.php" role="php" />
+ </dir> <!-- /lib/Horde -->
+ </dir> <!-- /lib -->
+ <dir name="tests">
+ <file name="bug_325.phpt" role="test" />
+ <file name="mail_001.phpt" role="test" />
+ <file name="mail_002.phpt" role="test" />
+ <file name="mail_003.phpt" role="test" />
+ <file name="mail_004.phpt" role="test" />
+ <file name="mail_005.phpt" role="test" />
+ <file name="mail_dummy.inc" role="test" />
+ </dir> <!-- /tests -->
+ </dir> <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.0</min>
+ </php>
+ <pearinstaller>
+ <min>1.5.0</min>
+ </pearinstaller>
+ <package>
+ <name>Mail_mimeDecode</name>
+ <channel>pear.php.net</channel>
+ </package>
+ <package>
+ <name>Util</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <extension>
+ <name>gettext</name>
+ </extension>
+ </required>
+ <optional>
+ <package>
+ <name>Auth</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Horde_Compress</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Horde_Framework</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>iCalendar</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Horde_Prefs</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Horde_SessionObjects</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ <package>
+ <name>Horde_Text_Filter</name>
+ <channel>pear.horde.org</channel>
+ </package>
+ </optional>
+ </dependencies>
+ <phprelease>
+ <filelist>
+ <install name="lib/Horde/MIME/Viewer/ooo/common.xsl" as="Horde/MIME/Viewer/ooo/common.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/global_document.xsl" as="Horde/MIME/Viewer/ooo/global_document.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/main_html.xsl" as="Horde/MIME/Viewer/ooo/main_html.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/palm.xsl" as="Horde/MIME/Viewer/ooo/palm.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/style_header.xsl" as="Horde/MIME/Viewer/ooo/style_header.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/style_inlined.xsl" as="Horde/MIME/Viewer/ooo/style_inlined.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/style_mapping.xsl" as="Horde/MIME/Viewer/ooo/style_mapping.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/table.xsl" as="Horde/MIME/Viewer/ooo/table.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/table_cells.xsl" as="Horde/MIME/Viewer/ooo/table_cells.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/table_columns.xsl" as="Horde/MIME/Viewer/ooo/table_columns.xsl" />
+ <install name="lib/Horde/MIME/Viewer/ooo/table_rows.xsl" as="Horde/MIME/Viewer/ooo/table_rows.xsl" />
+ <install name="lib/Horde/MIME/Viewer/Driver.php" as="Horde/MIME/Viewer/Driver.php" />
+ <install name="lib/Horde/MIME/Viewer/audio.php" as="Horde/MIME/Viewer/audio.php" />
+ <install name="lib/Horde/MIME/Viewer/css.php" as="Horde/MIME/Viewer/css.php" />
+ <install name="lib/Horde/MIME/Viewer/deb.php" as="Horde/MIME/Viewer/deb.php" />
+ <install name="lib/Horde/MIME/Viewer/default.php" as="Horde/MIME/Viewer/default.php" />
+ <install name="lib/Horde/MIME/Viewer/enriched.php" as="Horde/MIME/Viewer/enriched.php" />
+ <install name="lib/Horde/MIME/Viewer/enscript.php" as="Horde/MIME/Viewer/enscript.php" />
+ <install name="lib/Horde/MIME/Viewer/html.php" as="Horde/MIME/Viewer/html.php" />
+ <install name="lib/Horde/MIME/Viewer/images.php" as="Horde/MIME/Viewer/images.php" />
+ <install name="lib/Horde/MIME/Viewer/msexcel.php" as="Horde/MIME/Viewer/msexcel.php" />
+ <install name="lib/Horde/MIME/Viewer/mspowerpoint.php" as="Horde/MIME/Viewer/mspowerpoint.php" />
+ <install name="lib/Horde/MIME/Viewer/msword.php" as="Horde/MIME/Viewer/msword.php" />
+ <install name="lib/Horde/MIME/Viewer/ooo.php" as="Horde/MIME/Viewer/ooo.php" />
+ <install name="lib/Horde/MIME/Viewer/pdf.php" as="Horde/MIME/Viewer/pdf.php" />
+ <install name="lib/Horde/MIME/Viewer/php.php" as="Horde/MIME/Viewer/php.php" />
+ <install name="lib/Horde/MIME/Viewer/plain.php" as="Horde/MIME/Viewer/plain.php" />
+ <install name="lib/Horde/MIME/Viewer/rar.php" as="Horde/MIME/Viewer/rar.php" />
+ <install name="lib/Horde/MIME/Viewer/report.php" as="Horde/MIME/Viewer/report.php" />
+ <install name="lib/Horde/MIME/Viewer/rfc822.php" as="Horde/MIME/Viewer/rfc822.php" />
+ <install name="lib/Horde/MIME/Viewer/richtext.php" as="Horde/MIME/Viewer/richtext.php" />
+ <install name="lib/Horde/MIME/Viewer/rpm.php" as="Horde/MIME/Viewer/rpm.php" />
+ <install name="lib/Horde/MIME/Viewer/rtf.php" as="Horde/MIME/Viewer/rtf.php" />
+ <install name="lib/Horde/MIME/Viewer/security.php" as="Horde/MIME/Viewer/security.php" />
+ <install name="lib/Horde/MIME/Viewer/simple.php" as="Horde/MIME/Viewer/simple.php" />
+ <install name="lib/Horde/MIME/Viewer/smil.php" as="Horde/MIME/Viewer/smil.php" />
+ <install name="lib/Horde/MIME/Viewer/source.php" as="Horde/MIME/Viewer/source.php" />
+ <install name="lib/Horde/MIME/Viewer/srchighlite.php" as="Horde/MIME/Viewer/srchighlite.php" />
+ <install name="lib/Horde/MIME/Viewer/tgz.php" as="Horde/MIME/Viewer/tgz.php" />
+ <install name="lib/Horde/MIME/Viewer/tnef.php" as="Horde/MIME/Viewer/tnef.php" />
+ <install name="lib/Horde/MIME/Viewer/vcard.php" as="Horde/MIME/Viewer/vcard.php" />
+ <install name="lib/Horde/MIME/Viewer/webcpp.php" as="Horde/MIME/Viewer/webcpp.php" />
+ <install name="lib/Horde/MIME/Viewer/wordperfect.php" as="Horde/MIME/Viewer/wordperfect.php" />
+ <install name="lib/Horde/MIME/Viewer/zip.php" as="Horde/MIME/Viewer/zip.php" />
+ <install name="lib/Horde/MIME/Address.php" as="Horde/MIME/Address.php" />
+ <install name="lib/Horde/MIME/Headers.php" as="Horde/MIME/Headers.php" />
+ <install name="lib/Horde/MIME/MDN.php" as="Horde/MIME/MDN.php" />
+ <install name="lib/Horde/MIME/Magic.php" as="Horde/MIME/Magic.php" />
+ <install name="lib/Horde/MIME/Mail.php" as="Horde/MIME/Mail.php" />
+ <install name="lib/Horde/MIME/Message.php" as="Horde/MIME/Message.php" />
+ <install name="lib/Horde/MIME/Part.php" as="Horde/MIME/Part.php" />
+ <install name="lib/Horde/MIME/Viewer.php" as="Horde/MIME/Viewer.php" />
+ <install name="lib/Horde/MIME/mime.magic.php" as="Horde/MIME/mime.magic.php" />
+ <install name="lib/Horde/MIME/mime.mapping.php" as="Horde/MIME/mime.mapping.php" />
+ <install name="lib/Horde/MIME.php" as="Horde/MIME.php" />
+ </filelist>
+ </phprelease>
+</package>
--- /dev/null
+Zwölf Boxkämpfer jagen Viktor quer über den großen Sylter Deich.
--- /dev/null
+--TEST--
+Bug #338 (fileinfo returning charset)
+--SKIPIF--
+<?php if (!extension_loaded('fileinfo')) echo 'skip'; ?>
+--FILE--
+<?php
+require_once 'Horde/Util.php';
+require dirname(__FILE__) . '/../lib/Horde/MIME/Magic.php';
+echo MIME_Magic::analyzeFile(dirname(__FILE__) . '/bug_325.txt');
+?>
+--EXPECT--
+text/plain
--- /dev/null
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
+tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
+proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
--- /dev/null
+--TEST--
+Bug #4834 Wrong encoding of email lists with groups.
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/../lib/Horde/MIME.php';
+echo MIME::encodeAddress('"John Doe" <john@example.com>, Group: peter@example.com, jane@example.com;');
+
+?>
+--EXPECT--
+John Doe <john@example.com>, Group: peter@example.com, jane@example.com;
--- /dev/null
+--TEST--
+Bug #6896 MIME::rfc822Explode parsing broken
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/../MIME.php';
+var_dump(MIME::rfc822Explode('addr1@example.com, addr2@example.com'));
+
+?>
+--EXPECT--
+array(2) {
+ [0]=>
+ string(17) "addr1@example.com"
+ [1]=>
+ string(18) " addr2@example.com"
+}
--- /dev/null
+--TEST--
+MIME_Contents tests.
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/../MIME/Contents.php';
+
+$_SERVER['SERVER_NAME'] = 'mail.example.com';
+$message = MIME_Structure::parseTextMIMEMessage(
+ file_get_contents(dirname(__FILE__) . '/contents1.eml'));
+$contents = new MIME_Contents($message);
+
+var_export($contents->getDownloadAllList());
+echo "\n";
+var_export($contents->getAttachmentContents());
+
+?>
+--EXPECT--
+array (
+ 0 => '2.0',
+)
+array (
+ 0 =>
+ array (
+ 'name' => 'Weitergeleitete Nachricht: Small message',
+ 'data' => 'Return-Path: <jan@horde.org>
+Received: from neo.wg.de ([unix socket])
+ by neo (Cyrus v2.2.13) with LMTPA;
+ Tue, 11 Mar 2008 17:26:11 +0100
+X-Sieve: CMU Sieve 2.2
+Received: from localhost (localhost [127.0.0.1])
+ by neo.wg.de (Postfix) with ESMTP id 142BF32B032
+ for <jan@localhost.wg.de>; Tue, 11 Mar 2008 17:26:11 +0100 (CET)
+Received: from neo.wg.de ([127.0.0.1])
+ by localhost (neo.wg.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP
+ id 02540-02 for <jan@localhost.wg.de>; Tue, 11 Mar 2008 17:26:02 +0100 (CET)
+Received: from localhost (localhost [127.0.0.1])
+ by neo.wg.de (Postfix) with ESMTP id 21E2532B037
+ for <jan@localhost>; Tue, 11 Mar 2008 17:26:02 +0100 (CET)
+Received: from 192.168.60.101 ([192.168.60.101]) by neo.wg.de (Horde
+ Framework) with HTTP; Tue, 11 Mar 2008 17:26:02 +0100
+Message-ID: <20080311172602.12293hbhf6ddsza0@neo.wg.de>
+X-Priority: 3 (Normal)
+Date: Tue, 11 Mar 2008 17:26:02 +0100
+From: Jan Schneider <jan@horde.org>
+To: "jan@localhost" <jan@wg.de>
+Subject: Small message
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=ISO-8859-1;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+User-Agent: Internet Messaging Program (IMP) H3 (5.0-cvs)
+X-Virus-Scanned: amavisd-new at wg.de
+X-Spam-Status: No, score=-4.351 required=5 tests=[ALL_TRUSTED=-1.8, AWL=0.048,
+ BAYES_00=-2.599]
+X-Spam-Score: -4.351
+X-Spam-Level:
+
+Small message text.
+
+
+',
+ ),
+)
--- /dev/null
+--TEST--
+MIME_Mail constructor test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail('My Subject', "This is\nthe body",
+ 'recipient@example.com', 'sender@example.com',
+ 'iso-8859-15');
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-15;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is
+the body
--- /dev/null
+--TEST--
+MIME_Mail methods test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail();
+$mail->addHeader('Subject', 'My Subject');
+$mail->setBody("This is\nthe body", 'iso-8859-15');
+$mail->addHeader('To', 'recipient@example.com');
+$mail->addHeader('Cc', 'null@example.com');
+$mail->addHeader('Bcc', 'invisible@example.com');
+$mail->addHeader('From', 'sender@example.com');
+$mail->removeHeader('Cc');
+
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-15;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is
+the body
--- /dev/null
+--TEST--
+MIME_Mail encoding test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+require 'Horde/NLS.php';
+
+$mail = new MIME_Mail('Schöner Betreff', "Hübsche Umlaute \n und Leerzeichen.",
+ 'Empfänger <recipient@example.com>',
+ 'sender@example.com', 'iso-8859-1');
+$mail->addHeader('Cc', 'Der schöne Peter <peter@example.com>', 'iso-8859-15');
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: =?iso-8859-1?b?U2No9m5lcg==?= Betreff
+To: =?iso-8859-1?b?RW1wZuRuZ2Vy?= <recipient@example.com>
+From: sender@example.com
+Cc: Der =?iso-8859-15?b?c2No9m5l?= Peter <peter@example.com>
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-1;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+H=FCbsche Umlaute
+ und Leerzeichen.
--- /dev/null
+--TEST--
+MIME_Mail::addPart() test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail('My Subject', "This is\nthe body",
+ 'recipient@example.com', 'sender@example.com',
+ 'iso-8859-15');
+$mail->addPart('text/plain', 'This is a plain text', 'iso-8859-1', 'inline');
+$mail->addPart('application/octet-stream',
+ file_get_contents(dirname(__FILE__) . '/attachment.bin'),
+ null, 'attachment');
+
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_%s"
+Content-Transfer-Encoding: 7bit
+
+This message is in MIME format.
+
+--=_%s
+Content-Type: text/plain;
+ charset=iso-8859-15;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is
+the body
+
+--=_%s
+Content-Type: text/plain;
+ charset=iso-8859-1
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is a plain text
+--=_%s
+Content-Type: application/octet-stream
+Content-Disposition: attachment
+Content-Transfer-Encoding: base64
+
+WnfDtmxmIEJveGvDpG1wZmVyIGphZ2VuIFZpa3RvciBxdWVyIMO8YmVyIGRlbiBncm/Dn2VuIFN5
+bHRlciBEZWljaC4K
+
+--=_%s--
--- /dev/null
+--TEST--
+MIME_Mail HTML test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail('My Subject', null, 'recipient@example.com',
+ 'sender@example.com');
+$mail->setBody("This is\nthe plain text body.");
+echo $mail->send('dummy');
+
+echo "====================================================================\n";
+
+$mail = new MIME_Mail('My Subject', null, 'recipient@example.com',
+ 'sender@example.com');
+$mail->setHTMLBody("<h1>Header Title</h1>\n<p>This is<br />the html text body.</p>",
+ 'iso-8859-1', false);
+echo $mail->send('dummy');
+
+echo "====================================================================\n";
+
+$mail = new MIME_Mail('My Subject', null, 'recipient@example.com',
+ 'sender@example.com');
+$mail->setHTMLBody("<h1>Header Title</h1>\n<p>This is<br />the html text body.</p>");
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-1;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is
+the plain text body.
+====================================================================
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/html;
+ charset=iso-8859-1
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+<h1>Header Title</h1>
+<p>This is<br />the html text body.</p>
+====================================================================
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: multipart/alternative;
+ boundary="=_%s"
+Content-Transfer-Encoding: 7bit
+
+This message is in MIME format.
+
+--=_%s
+Content-Type: text/plain;
+ charset=iso-8859-1;
+ DelSp="Yes";
+ format="flowed"
+Content-Description: Plaintext Version of Message
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+
+
+HEADER TITLE
+
+This is
+the html text body.
+
+--=_%s
+Content-Type: text/html;
+ charset=iso-8859-1
+Content-Description: HTML Version of Message
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+<h1>Header Title</h1>
+<p>This is<br />the html text body.</p>
+--=_%s--
--- /dev/null
+--TEST--
+MIME_Mail::addAttachment() test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail('My Subject', "This is\nthe body",
+ 'recipient@example.com', 'sender@example.com',
+ 'iso-8859-15');
+$mail->addAttachment(dirname(__FILE__) . '/attachment.bin');
+$mail->addAttachment(dirname(__FILE__) . '/mail_dummy.inc', 'my_name.html', 'text/html', 'iso-8859-15');
+
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="=_%s"
+Content-Transfer-Encoding: 7bit
+
+This message is in MIME format.
+
+--=_%s
+Content-Type: text/plain;
+ charset=iso-8859-15;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is
+the body
+
+--=_%s
+Content-Type: application/octet-stream;
+ name="attachment.bin"
+Content-Disposition: attachment;
+ filename="attachment.bin"
+Content-Transfer-Encoding: base64
+
+WnfDtmxmIEJveGvDpG1wZmVyIGphZ2VuIFZpa3RvciBxdWVyIMO8YmVyIGRlbiBncm/Dn2VuIFN5
+bHRlciBEZWljaC4K
+
+--=_%s
+Content-Type: text/html;
+ charset=iso-8859-15;
+ name="my_name.html"
+Content-Disposition: attachment;
+ filename="my_name.html"
+Content-Transfer-Encoding: 7bit
+
+<?php
+/**
+ * @package Mail
+ */
+
+require dirname(__FILE__) . '/../MIME/Mail.php';
+$_SERVER['SERVER_NAME'] = 'mail.example.com';
+
+class Mail_dummy extends Mail {
+ function send($recipients, $headers, $body)
+ {
+ list(,$text_headers) = Mail::prepareHeaders($headers);
+ return $text_headers . "\n\n" . $body;
+ }
+}
+
+--=_%s--
--- /dev/null
+--TEST--
+MIME_Mail reusing test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail('My Subject', "This is\nthe body",
+ 'recipient@example.com', 'sender@example.com',
+ 'iso-8859-15');
+echo $mail->send('dummy');
+$id = $mail->_headers->getValue('message-id');
+
+echo "====================================================================\n";
+
+$mail->addHeader('To', 'Änderung <other@example.com>', 'utf-8');
+echo $mail->send('dummy');
+
+echo "====================================================================\n";
+
+var_dump($id != $mail->_headers->getValue('message-id'));
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-15;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+This is
+the body
+====================================================================
+Subject: My Subject
+From: sender@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-15;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+To: =?utf-8?b?w4RuZGVydW5n?= <other@example.com>
+
+This is
+the body
+
+====================================================================
+bool(true)
+
--- /dev/null
+--TEST--
+MIME_Mail flowed text test
+--FILE--
+<?php
+
+require dirname(__FILE__) . '/mail_dummy.inc';
+
+$mail = new MIME_Mail();
+$mail->addHeader('Subject', 'My Subject');
+$mail->addHeader('To', 'recipient@example.com');
+$mail->setBody('Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.');
+
+echo $mail->send('dummy');
+
+?>
+--EXPECTF--
+Subject: My Subject
+To: recipient@example.com
+Message-ID: <%d.%s@mail.example.com>
+User-Agent: Horde Application Framework 3.2
+Date: %s, %d %s %d %d:%d:%d %s%d
+MIME-Version: 1.0
+Content-Type: text/plain;
+ charset=iso-8859-1;
+ DelSp="Yes";
+ format="flowed"
+Content-Disposition: inline
+Content-Transfer-Encoding: 7bit
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
+eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
+minim veniam, quis nostrud exercitation ullamco laboris nisi ut
+aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat
+cupidatat non proident, sunt in culpa qui officia deserunt mollit anim
+id est laborum.
\ No newline at end of file
--- /dev/null
+<?php
+/**
+ * @package Mail
+ */
+
+require dirname(__FILE__) . '/../MIME/Mail.php';
+$_SERVER['SERVER_NAME'] = 'mail.example.com';
+
+class Mail_dummy extends Mail {
+ function send($recipients, $headers, $body)
+ {
+ list(,$text_headers) = Mail::prepareHeaders($headers);
+ return $text_headers . "\n\n" . $body;
+ }
+}
--- /dev/null
+--TEST--
+MIME_Viewer_html: URL dereferer tests
+--FILE--
+<?php
+
+define('HORDE_BASE', dirname(__FILE__) . '/../../..');
+require_once dirname(__FILE__) . '/../MIME/Viewer.php';
+require_once dirname(__FILE__) . '/../MIME/Viewer/html.php';
+require_once 'Horde.php';
+
+class Registry {
+ function get($param, $app = null)
+ {
+ if ($param == 'webroot' || $app == 'horde') {
+ return '/horde';
+ }
+ die("Can't emulate Registry. \$param: $param, \$app: $app");
+ }
+}
+
+class Browser {
+ function isBrowser($agent)
+ {
+ return $agent == 'msie';
+ }
+}
+
+$conf['server']['name'] = 'www.example.com';
+$conf['server']['port'] = 80;
+$conf['use_ssl'] = 0;
+$registry = new Registry();
+$browser = new Browser();
+$viewer = new MIME_Viewer_html($null);
+
+for ($i = 1; $i <= 7; $i++) {
+ $data = file_get_contents(dirname(__FILE__) . '/url' . $i . '.html');
+ echo $viewer->_cleanHTML($data);
+}
+
+?>
+--EXPECT--
+<A href="http://www.example.com/horde/services/go.php?url=http%3A%2F%2F66.102.7.147%2F">link</A>
+<A href="http://www.example.com/horde/services/go.php?url=http%3A%2F%2F%2577%2577%2577%252E%2567%256F%256F%2567%256C%2565%252E%2563%256F%256D">link</A>
+<A href="http://www.example.com/horde/services/go.php?url=ht%3A%2F%2Fwww.google.com%2F">link</A>
+<A href="http://www.example.com/horde/services/go.php?url=http%3A%2F%2Fgoogle.com%2F">link</A>
+<A href="http://www.example.com/horde/services/go.php?url=http%3A%2F%2Fwww.google.com.%2F">link</A>
+<A href="http://www.example.com/horde/services/go.php?url=XSSCleaneddocument.location%3D%27http%3A%2F%2Fwww.google.com%2F%27">link</A>
+<A href="http://www.example.com/horde/services/go.php?url=http%3A%2F%2Fwww.gohttp%3A%2F%2Fwww.google.com%2Fogle.com%2F">link</A>
--- /dev/null
+<A HREF=http://66.102.7.147/>link</A>
--- /dev/null
+<A HREF=http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D>link</A>
--- /dev/null
+<A HREF=ht://www.google.com/>link</A>
--- /dev/null
+<A HREF=http://google.com/>link</A>
--- /dev/null
+<A HREF=http://www.google.com./>link</A>
--- /dev/null
+<A HREF="javascript:document.location='http://www.google.com/'">link</A>
--- /dev/null
+<A HREF=http://www.gohttp://www.google.com/ogle.com/>link</A>
--- /dev/null
+--TEST--
+PHP source viewer
+--FILE--
+<?php
+
+require_once 'Horde/MIME/Viewer.php';
+require_once 'Horde/MIME/Viewer/php.php';
+
+$viewer = new MIME_Viewer_php($null);
+
+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');
+echo $viewer->lineNumber(str_replace('<?php ', '', highlight_string('<?php highlight_file(__FILE__);', true)));
+?>
+--EXPECT--
+<ol class="code-listing striped">
+<li id="l1"><span class="default">highlight_file</span><span class="keyword">(</span><span class="default">__FILE__</span><span class="keyword">);</span></li>
+</ol>
\ No newline at end of file