From 4af76d20cafcd456bf3ce0fcb17b25a888c45160 Mon Sep 17 00:00:00 2001 From: Thomas <thomas@roundcube.net> Date: Mon, 21 Oct 2013 15:14:46 -0400 Subject: [PATCH] Bump version --- program/lib/Roundcube/rcube_mime.php | 272 ++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 183 insertions(+), 89 deletions(-) diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php index 17cb3f0..323a5e9 100644 --- a/program/lib/Roundcube/rcube_mime.php +++ b/program/lib/Roundcube/rcube_mime.php @@ -2,8 +2,6 @@ /* +-----------------------------------------------------------------------+ - | program/include/rcube_mime.php | - | | | This file is part of the Roundcube Webmail client | | Copyright (C) 2005-2012, The Roundcube Dev Team | | Copyright (C) 2011-2012, Kolab Systems AG | @@ -14,13 +12,11 @@ | | | PURPOSE: | | MIME message parsing utilities | - | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli <roundcube@gmail.com> | | Author: Aleksander Machniak <alec@alec.pl> | +-----------------------------------------------------------------------+ */ - /** * Class for parsing MIME messages @@ -131,10 +127,11 @@ * @param int $max List only this number of addresses * @param boolean $decode Decode address strings * @param string $fallback Fallback charset if none specified + * @param boolean $addronly Return flat array with e-mail addresses only * - * @return array Indexed list of addresses + * @return array Indexed list of addresses */ - static function decode_address_list($input, $max = null, $decode = true, $fallback = null) + static function decode_address_list($input, $max = null, $decode = true, $fallback = null, $addronly = false) { $a = self::parse_address_list($input, $decode, $fallback); $out = array(); @@ -149,20 +146,21 @@ foreach ($a as $val) { $j++; $address = trim($val['address']); - $name = trim($val['name']); - if ($name && $address && $name != $address) - $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); - else if ($address) - $string = $address; - else if ($name) - $string = $name; + if ($addronly) { + $out[$j] = $address; + } + else { + $name = trim($val['name']); + if ($name && $address && $name != $address) + $string = sprintf('%s <%s>', preg_match("/$special_chars/", $name) ? '"'.addcslashes($name, '"').'"' : $name, $address); + else if ($address) + $string = $address; + else if ($name) + $string = $name; - $out[$j] = array( - 'name' => $name, - 'mailto' => $address, - 'string' => $string - ); + $out[$j] = array('name' => $name, 'mailto' => $address, 'string' => $string); + } if ($max && $j==$max) break; @@ -363,6 +361,11 @@ $address = $m[1]; $name = ''; } + // special case (#1489092) + else if (preg_match('/(\s*<MAILER-DAEMON>)$/', $val, $m)) { + $address = 'MAILER-DAEMON'; + $name = substr($val, 0, -strlen($m[1])); + } else { $name = $val; } @@ -480,13 +483,20 @@ $q_level = 0; foreach ($text as $idx => $line) { - if ($line[0] == '>' && preg_match('/^(>+\s*)/', $line, $regs)) { - $q = strlen(str_replace(' ', '', $regs[0])); - $line = substr($line, strlen($regs[0])); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $q = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); + // remove (optional) space-staffing + $line = preg_replace('/^ /', '', $line); - if ($q == $q_level && $line - && isset($text[$last]) - && $text[$last][strlen($text[$last])-1] == ' ' + // The same paragraph (We join current line with the previous one) when: + // - the same level of quoting + // - previous line was flowed + // - previous line contains more than only one single space (and quote char(s)) + if ($q == $q_level + && isset($text[$last]) && $text[$last][strlen($text[$last])-1] == ' ' + && !preg_match('/^>+ {0,1}$/', $text[$last]) ) { $text[$last] .= $line; unset($text[$idx]); @@ -539,10 +549,13 @@ foreach ($text as $idx => $line) { if ($line != '-- ') { - if ($line[0] == '>' && preg_match('/^(>+ {0,1})+/', $line, $regs)) { - $level = substr_count($regs[0], '>'); + if (preg_match('/^(>+)/', $line, $m)) { + // remove quote chars + $level = strlen($m[1]); + $line = preg_replace('/^>+/', '', $line); + // remove (optional) space-staffing and spaces before the line end + $line = preg_replace('/(^ | +$)/', '', $line); $prefix = str_repeat('>', $level) . ' '; - $line = rtrim(substr($line, strlen($regs[0]))); $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix", false, $charset); } else if ($line) { @@ -560,81 +573,122 @@ /** - * Improved wordwrap function. + * Improved wordwrap function with multibyte support. + * The code is based on Zend_Text_MultiByte::wordWrap(). * - * @param string $string Text to wrap - * @param int $width Line width - * @param string $break Line separator - * @param bool $cut Enable to cut word - * @param string $charset Charset of $string + * @param string $string Text to wrap + * @param int $width Line width + * @param string $break Line separator + * @param bool $cut Enable to cut word + * @param string $charset Charset of $string + * @param bool $wrap_quoted When enabled quoted lines will not be wrapped * * @return string Text */ - public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null) + public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null, $wrap_quoted=true) { - if ($charset && function_exists('mb_internal_encoding')) { + // Note: Never try to use iconv instead of mbstring functions here + // Iconv's substr/strlen are 100x slower (#1489113) + + if ($charset && $charset != RCUBE_CHARSET && function_exists('mb_internal_encoding')) { mb_internal_encoding($charset); } - $para = preg_split('/\r?\n/', $string); - $string = ''; + // Convert \r\n to \n, this is our line-separator + $string = str_replace("\r\n", "\n", $string); + $separator = "\n"; // must be 1 character length + $result = array(); - while (count($para)) { - $line = array_shift($para); - if ($line[0] == '>') { - $string .= $line.$break; - continue; - } + while (($stringLength = mb_strlen($string)) > 0) { + $breakPos = mb_strpos($string, $separator, 0); - $list = explode(' ', $line); - $len = 0; - while (count($list)) { - $line = array_shift($list); - $l = mb_strlen($line); - $newlen = $len + $l + ($len ? 1 : 0); - - if ($newlen <= $width) { - $string .= ($len ? ' ' : '').$line; - $len += (1 + $l); + // quoted line (do not wrap) + if ($wrap_quoted && $string[0] == '>') { + if ($breakPos === $stringLength - 1 || $breakPos === false) { + $subString = $string; + $cutLength = null; } else { - if ($l > $width) { - if ($cut) { - $start = 0; - while ($l) { - $str = mb_substr($line, $start, $width); - $strlen = mb_strlen($str); - $string .= ($len ? $break : '').$str; - $start += $strlen; - $l -= $strlen; - $len = $strlen; + $subString = mb_substr($string, 0, $breakPos); + $cutLength = $breakPos + 1; + } + } + // next line found and current line is shorter than the limit + else if ($breakPos !== false && $breakPos < $width) { + if ($breakPos === $stringLength - 1) { + $subString = $string; + $cutLength = null; + } + else { + $subString = mb_substr($string, 0, $breakPos); + $cutLength = $breakPos + 1; + } + } + else { + $subString = mb_substr($string, 0, $width); + + // last line + if ($breakPos === false && $subString === $string) { + $cutLength = null; + } + else { + $nextChar = mb_substr($string, $width, 1); + + if ($nextChar === ' ' || $nextChar === $separator) { + $afterNextChar = mb_substr($string, $width + 1, 1); + + // Note: mb_substr() does never return False + if ($afterNextChar === false || $afterNextChar === '') { + $subString .= $nextChar; + } + + $cutLength = mb_strlen($subString) + 1; + } + else { + $spacePos = mb_strrpos($subString, ' ', 0); + + if ($spacePos !== false) { + $subString = mb_substr($subString, 0, $spacePos); + $cutLength = $spacePos + 1; + } + else if ($cut === false) { + $spacePos = mb_strpos($string, ' ', 0); + + if ($spacePos !== false && ($breakPos === false || $spacePos < $breakPos)) { + $subString = mb_substr($string, 0, $spacePos); + $cutLength = $spacePos + 1; + } + else if ($breakPos === false) { + $subString = $string; + $cutLength = null; + } + else { + $subString = mb_substr($string, 0, $breakPos); + $cutLength = $breakPos + 1; } } else { - $string .= ($len ? $break : '').$line; - if (count($list)) { - $string .= $break; - } - $len = 0; + $cutLength = $width; } - } - else { - $string .= $break.$line; - $len = $l; } } } - if (count($para)) { - $string .= $break; + $result[] = $subString; + + if ($cutLength !== null) { + $string = mb_substr($string, $cutLength, ($stringLength - $cutLength)); + } + else { + break; } } - if ($charset && function_exists('mb_internal_encoding')) { + if ($charset && $charset != RCUBE_CHARSET && function_exists('mb_internal_encoding')) { mb_internal_encoding(RCUBE_CHARSET); } - return $string; + return implode($break, $result); } @@ -667,7 +721,16 @@ // try fileinfo extension if available if (!$mime_type && function_exists('finfo_open')) { - if ($finfo = finfo_open(FILEINFO_MIME, $mime_magic)) { + // null as a 2nd argument should be the same as no argument + // this however is not true on all systems/versions + if ($mime_magic) { + $finfo = finfo_open(FILEINFO_MIME, $mime_magic); + } + else { + $finfo = finfo_open(FILEINFO_MIME); + } + + if ($finfo) { if ($is_stream) $mime_type = finfo_buffer($finfo, $path); else @@ -713,20 +776,27 @@ // load mapping file $file_paths = array(); - if ($mime_types = rcube::get_instance()->config->get('mime_types')) + if ($mime_types = rcube::get_instance()->config->get('mime_types')) { $file_paths[] = $mime_types; + } // try common locations - $file_paths[] = '/etc/httpd/mime.types'; - $file_paths[] = '/etc/httpd2/mime.types'; - $file_paths[] = '/etc/apache/mime.types'; - $file_paths[] = '/etc/apache2/mime.types'; - $file_paths[] = '/usr/local/etc/httpd/conf/mime.types'; - $file_paths[] = '/usr/local/etc/apache/conf/mime.types'; + if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { + $file_paths[] = 'C:/xampp/apache/conf/mime.types.'; + } + else { + $file_paths[] = '/etc/mime.types'; + $file_paths[] = '/etc/httpd/mime.types'; + $file_paths[] = '/etc/httpd2/mime.types'; + $file_paths[] = '/etc/apache/mime.types'; + $file_paths[] = '/etc/apache2/mime.types'; + $file_paths[] = '/usr/local/etc/httpd/conf/mime.types'; + $file_paths[] = '/usr/local/etc/apache/conf/mime.types'; + } foreach ($file_paths as $fp) { - if (is_readable($fp)) { - $lines = file($fp, FILE_IGNORE_NEW_LINES); + if (@is_readable($fp)) { + $lines = file($fp, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); break; } } @@ -748,11 +818,35 @@ // fallback to some well-known types most important for daily emails if (empty($mime_types)) { - $mime_extensions = @include(RCUBE_CONFIG_DIR . '/mimetypes.php'); - $mime_extensions += array('gif' => 'image/gif', 'png' => 'image/png', 'jpg' => 'image/jpg', 'jpeg' => 'image/jpeg', 'tif' => 'image/tiff'); + $mime_extensions = (array) @include(RCUBE_CONFIG_DIR . '/mimetypes.php'); - foreach ($mime_extensions as $ext => $mime) + foreach ($mime_extensions as $ext => $mime) { $mime_types[$mime][] = $ext; + } + } + + // Add some known aliases that aren't included by some mime.types (#1488891) + // the order is important here so standard extensions have higher prio + $aliases = array( + 'image/gif' => array('gif'), + 'image/png' => array('png'), + 'image/x-png' => array('png'), + 'image/jpeg' => array('jpg', 'jpeg', 'jpe'), + 'image/jpg' => array('jpg', 'jpeg', 'jpe'), + 'image/pjpeg' => array('jpg', 'jpeg', 'jpe'), + 'image/tiff' => array('tif'), + 'message/rfc822' => array('eml'), + 'text/x-mail' => array('eml'), + ); + + foreach ($aliases as $mime => $exts) { + $mime_types[$mime] = array_unique(array_merge((array) $mime_types[$mime], $exts)); + + foreach ($exts as $ext) { + if (!isset($mime_extensions[$ext])) { + $mime_extensions[$ext] = $mime; + } + } } return $mimetype ? $mime_types[$mimetype] : $mime_extensions; -- Gitblit v1.9.1