Page MenuHomePhorge

No OneTemporary

Size
92 KB
Referenced Files
None
Subscribers
None
This document is not UTF8. It was detected as ISO-8859-1 (Latin 1) and converted to UTF8 for display.
diff --git a/program/include/rcube_bc.inc b/program/include/rcube_bc.inc
index 1894873e6..4130d4dc5 100644
--- a/program/include/rcube_bc.inc
+++ b/program/include/rcube_bc.inc
@@ -1,384 +1,384 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/include/main.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2012, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Provide deprecated functions aliases for backward compatibility |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Roundcube Webmail deprecated functions
*
* @package Core
* @author Thomas Bruederli <roundcube@gmail.com>
*/
// constants for input reading
define('RCUBE_INPUT_GET', rcube_utils::INPUT_GET);
define('RCUBE_INPUT_POST', rcube_utils::INPUT_POST);
define('RCUBE_INPUT_GPC', rcube_utils::INPUT_GPC);
define('JS_OBJECT_NAME', rcmail::JS_OBJECT_NAME);
function get_table_name($table)
{
return rcmail::get_instance()->db->table_name($table);
}
function rcube_label($p, $domain=null)
{
return rcmail::get_instance()->gettext($p, $domain);
}
function rcube_label_exists($name, $domain=null, &$ref_domain = null)
{
return rcmail::get_instance()->text_exists($name, $domain, $ref_domain);
}
function rcmail_overwrite_action($action)
{
rcmail::get_instance()->overwrite_action($action);
}
function rcmail_url($action, $p=array(), $task=null)
{
return rcmail::get_instance()->url((array)$p + array('_action' => $action, 'task' => $task));
}
function rcmail_temp_gc()
{
$rcmail = rcmail::get_instance()->temp_gc();
}
function rcube_charset_convert($str, $from, $to=NULL)
{
return rcube_charset::convert($str, $from, $to);
}
function rc_detect_encoding($string, $failover='')
{
return rcube_charset::detect($string, $failover);
}
function rc_utf8_clean($input)
{
return rcube_charset::clean($input);
}
function json_serialize($input)
{
return rcube_output::json_serialize($input);
}
function rep_specialchars_output($str, $enctype='', $mode='', $newlines=TRUE)
{
return rcube_utils::rep_specialchars_output($str, $enctype, $mode, $newlines);
}
function Q($str, $mode='strict', $newlines=TRUE)
{
return rcmail::Q($str, $mode, $newlines);
}
function JQ($str)
{
return rcmail::JQ($str);
}
function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
{
return rcube_utils::get_input_value($fname, $source, $allow_html, $charset);
}
function parse_input_value($value, $allow_html=FALSE, $charset=NULL)
{
return rcube_utils::parse_input_value($value, $allow_html, $charset);
}
function request2param($mode = RCUBE_INPUT_GPC, $ignore = 'task|action')
{
return rcube_utils::request2param($mode, $ignore);
}
function html_identifier($str, $encode=false)
{
return rcube_utils::html_identifier($str, $encode);
}
function rcube_table_output($attrib, $table_data, $a_show_cols, $id_col)
{
return rcmail::get_instance()->table_output($attrib, $table_data, $a_show_cols, $id_col);
}
function rcmail_get_edit_field($col, $value, $attrib, $type='text')
{
return rcube_utils::get_edit_field($col, $value, $attrib, $type);
}
function rcmail_mod_css_styles($source, $container_id, $allow_remote=false)
{
return rcube_utils::mod_css_styles($source, $container_id, $allow_remote);
}
function rcmail_xss_entity_decode($content)
{
return rcube_utils::xss_entity_decode($content);
}
function create_attrib_string($attrib, $allowed_attribs=array('id', 'class', 'style'))
{
return html::attrib_string($attrib, $allowed_attribs);
}
function parse_attrib_string($str)
{
return html::parse_attrib_string($str);
}
function format_date($date, $format=NULL, $convert=true)
{
return rcmail::get_instance()->format_date($date, $format, $convert);
}
function rcmail_mailbox_list($attrib)
{
return rcmail::get_instance()->folder_list($attrib);
}
function rcmail_mailbox_select($attrib = array())
{
return rcmail::get_instance()->folder_selector($attrib);
}
function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $attrib, $nestLevel = 0)
{
return rcmail::get_instance()->render_folder_tree_html($arrFolders, $mbox_name, $jslist, $attrib, $nestLevel);
}
function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames = false, $nestLevel = 0, $opts = array())
{
return rcmail::get_instance()->render_folder_tree_select($arrFolders, $mbox_name, $maxlength, $select, $realnames, $nestLevel, $opts);
}
function rcmail_build_folder_tree(&$arrFolders, $folder, $delm = '/', $path = '')
{
return rcmail::get_instance()->build_folder_tree($arrFolders, $folder, $delm, $path);
}
function rcmail_folder_classname($folder_id)
{
return rcmail::get_instance()->folder_classname($folder_id);
}
function rcmail_localize_foldername($name)
{
return rcmail::get_instance()->localize_foldername($name);
}
function rcmail_localize_folderpath($path)
{
return rcmail::get_instance()->localize_folderpath($path);
}
function rcmail_quota_display($attrib)
{
return rcmail::get_instance()->quota_display($attrib);
}
function rcmail_quota_content($attrib = null)
{
return rcmail::get_instance()->quota_content($attrib);
}
function rcmail_display_server_error($fallback=null, $fallback_args=null)
{
rcmail::get_instance()->display_server_error($fallback, $fallback_args);
}
function rcmail_filetype2classname($mimetype, $filename)
{
return rcube_utils::file2class($mimetype, $filename);
}
function rcube_html_editor($mode='')
{
rcmail::get_instance()->html_editor($mode);
}
function rcmail_replace_emoticons($html)
{
return rcmail::get_instance()->replace_emoticons($html);
}
function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file=null, $smtp_opts=null)
{
return rcmail::get_instance()->deliver_message($message, $from, $mailto, $smtp_error, $body_file, $smtp_opts);
}
function rcmail_gen_message_id()
{
return rcmail::get_instance()->gen_message_id();
}
function rcmail_user_date()
{
return rcmail::get_instance()->user_date();
}
function rcmail_mem_check($need)
{
return rcube_utils::mem_check($need);
}
function rcube_https_check($port=null, $use_https=true)
{
return rcube_utils::https_check($port, $use_https);
}
function rcube_sess_unset($var_name=null)
{
rcmail::get_instance()->session->remove($var_name);
}
function rcube_parse_host($name, $host='')
{
return rcube_utils::parse_host($name, $host);
}
function check_email($email, $dns_check=true)
{
return rcube_utils::check_email($email, $dns_check);
}
function console()
{
call_user_func_array(array('rcmail', 'console'), func_get_args());
}
function write_log($name, $line)
{
return rcmail::write_log($name, $line);
}
function rcmail_log_login()
{
return rcmail::get_instance()->log_login();
}
function rcmail_remote_ip()
{
return rcube_utils::remote_ip();
}
function rcube_check_referer()
{
return rcmail::check_referer();
}
function rcube_timer()
{
return rcmail::timer();
}
function rcube_print_time($timer, $label='Timer', $dest='console')
{
rcmail::print_timer($timer, $label, $dest);
}
function raise_error($arg=array(), $log=false, $terminate=false)
{
rcmail::raise_error($arg, $log, $terminate);
}
function rcube_log_bug($arg_arr)
{
rcmail::log_bug($arg_arr);
}
function rcube_upload_progress()
{
rcmail::get_instance()->upload_progress();
}
function rcube_upload_init()
{
return rcmail::get_instance()->upload_init();
}
function rcube_autocomplete_init()
{
rcmail::get_instance()->autocomplete_init();
}
function rcube_fontdefs($font = null)
{
return rcmail::font_defs($font);
}
function send_nocacheing_headers()
{
return rcmail::get_instance()->output->nocacheing_headers();
}
function show_bytes($bytes)
{
return rcmail::get_instance()->show_bytes($bytes);
}
-function rc_wordwrap($string, $width=75, $break="\n", $cut=false)
+function rc_wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null)
{
- return rcube_mime::wordwrap($string, $width, $break, $cut);
+ return rcube_mime::wordwrap($string, $width, $break, $cut, $charset);
}
function rc_request_header($name)
{
return rcube_utils::request_header($name);
}
function rcube_explode_quoted_string($delimiter, $string)
{
return rcube_utils::explode_quoted_string($delimiter, $string);
}
function rc_mime_content_type($path, $name, $failover = 'application/octet-stream', $is_stream=false)
{
return rcube_mime::file_content_type($path, $name, $failover, $is_stream);
}
function rc_image_content_type($data)
{
return rcube_mime::image_content_type($data);
}
function rcube_strtotime($date)
{
return rcube_utils::strtotime($date);
}
function rcube_idn_to_ascii($str)
{
return rcube_utils::idn_to_ascii($str);
}
function rcube_idn_to_utf8($str)
{
return rcube_utils::idn_to_utf8($str);
}
function send_future_expire_header($offset = 2600000)
{
return rcmail::get_instance()->output->future_expire_header($offset);
}
diff --git a/program/include/rcube_mime.php b/program/include/rcube_mime.php
index 2dbeaf67a..ac3ef9e8a 100644
--- a/program/include/rcube_mime.php
+++ b/program/include/rcube_mime.php
@@ -1,705 +1,713 @@
<?php
/*
+-----------------------------------------------------------------------+
| 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 |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| MIME message parsing utilities |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Class for parsing MIME messages
*
* @package Framework
* @subpackage Storage
* @author Thomas Bruederli <roundcube@gmail.com>
* @author Aleksander Machniak <alec@alec.pl>
*/
class rcube_mime
{
private static $default_charset;
/**
* Object constructor.
*/
function __construct($default_charset = null)
{
self::$default_charset = $default_charset;
}
/**
* Returns message/object character set name
*
* @return string Characted set name
*/
public static function get_charset()
{
if (self::$default_charset) {
return self::$default_charset;
}
if ($charset = rcube::get_instance()->config->get('default_charset')) {
return $charset;
}
return RCMAIL_CHARSET;
}
/**
* Parse the given raw message source and return a structure
* of rcube_message_part objects.
*
* It makes use of the PEAR:Mail_mimeDecode library
*
* @param string The message source
* @return object rcube_message_part The message structure
*/
public static function parse_message($raw_body)
{
$mime = new Mail_mimeDecode($raw_body);
$struct = $mime->decode(array('include_bodies' => true, 'decode_bodies' => true));
return self::structure_part($struct);
}
/**
* Recursive method to convert a Mail_mimeDecode part into a rcube_message_part object
*
* @param object A message part struct
* @param int Part count
* @param string Parent MIME ID
*
* @return object rcube_message_part
*/
private static function structure_part($part, $count=0, $parent='')
{
$struct = new rcube_message_part;
$struct->mime_id = $part->mime_id ? $part->mime_id : (empty($parent) ? (string)$count : "$parent.$count");
$struct->headers = $part->headers;
$struct->ctype_primary = $part->ctype_primary;
$struct->ctype_secondary = $part->ctype_secondary;
$struct->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
$struct->ctype_parameters = $part->ctype_parameters;
if ($part->headers['content-transfer-encoding'])
$struct->encoding = $part->headers['content-transfer-encoding'];
if ($part->ctype_parameters['charset'])
$struct->charset = $part->ctype_parameters['charset'];
$part_charset = $struct->charset ? $struct->charset : self::get_charset();
// determine filename
if (($filename = $part->d_parameters['filename']) || ($filename = $part->ctype_parameters['name'])) {
$struct->filename = rcube_mime::decode_mime_string($filename, $part_charset);
}
// copy part body and convert it to UTF-8 if necessary
$struct->body = $part->ctype_primary == 'text' || !$part->ctype_parameters['charset'] ? rcube_charset::convert($part->body, $part_charset) : $part->body;
$struct->size = strlen($part->body);
$struct->disposition = $part->disposition;
foreach ((array)$part->parts as $child_part) {
$struct->parts[] = self::structure_part($child_part, ++$count, $struct->mime_id);
}
return $struct;
}
/**
* Split an address list into a structured array list
*
* @param string $input Input string
* @param int $max List only this number of addresses
* @param boolean $decode Decode address strings
* @param string $fallback Fallback charset if none specified
*
* @return array Indexed list of addresses
*/
static function decode_address_list($input, $max = null, $decode = true, $fallback = null)
{
$a = self::parse_address_list($input, $decode, $fallback);
$out = array();
$j = 0;
// Special chars as defined by RFC 822 need to in quoted string (or escaped).
$special_chars = '[\(\)\<\>\\\.\[\]@,;:"]';
if (!is_array($a))
return $out;
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;
$out[$j] = array(
'name' => $name,
'mailto' => $address,
'string' => $string
);
if ($max && $j==$max)
break;
}
return $out;
}
/**
* Decode a message header value
*
* @param string $input Header value
* @param string $fallback Fallback charset if none specified
*
* @return string Decoded string
*/
public static function decode_header($input, $fallback = null)
{
$str = self::decode_mime_string((string)$input, $fallback);
return $str;
}
/**
* Decode a mime-encoded string to internal charset
*
* @param string $input Header value
* @param string $fallback Fallback charset if none specified
*
* @return string Decoded string
*/
public static function decode_mime_string($input, $fallback = null)
{
$default_charset = !empty($fallback) ? $fallback : self::get_charset();
// rfc: all line breaks or other characters not found
// in the Base64 Alphabet must be ignored by decoding software
// delete all blanks between MIME-lines, differently we can
// receive unnecessary blanks and broken utf-8 symbols
$input = preg_replace("/\?=\s+=\?/", '?==?', $input);
// encoded-word regexp
$re = '/=\?([^?]+)\?([BbQq])\?([^\n]*?)\?=/';
// Find all RFC2047's encoded words
if (preg_match_all($re, $input, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
// Initialize variables
$tmp = array();
$out = '';
$start = 0;
foreach ($matches as $idx => $m) {
$pos = $m[0][1];
$charset = $m[1][0];
$encoding = $m[2][0];
$text = $m[3][0];
$length = strlen($m[0][0]);
// Append everything that is before the text to be decoded
if ($start != $pos) {
$substr = substr($input, $start, $pos-$start);
$out .= rcube_charset::convert($substr, $default_charset);
$start = $pos;
}
$start += $length;
// Per RFC2047, each string part "MUST represent an integral number
// of characters . A multi-octet character may not be split across
// adjacent encoded-words." However, some mailers break this, so we
// try to handle characters spanned across parts anyway by iterating
// through and aggregating sequential encoded parts with the same
// character set and encoding, then perform the decoding on the
// aggregation as a whole.
$tmp[] = $text;
if ($next_match = $matches[$idx+1]) {
if ($next_match[0][1] == $start
&& $next_match[1][0] == $charset
&& $next_match[2][0] == $encoding
) {
continue;
}
}
$count = count($tmp);
$text = '';
// Decode and join encoded-word's chunks
if ($encoding == 'B' || $encoding == 'b') {
// base64 must be decoded a segment at a time
for ($i=0; $i<$count; $i++)
$text .= base64_decode($tmp[$i]);
}
else { //if ($encoding == 'Q' || $encoding == 'q') {
// quoted printable can be combined and processed at once
for ($i=0; $i<$count; $i++)
$text .= $tmp[$i];
$text = str_replace('_', ' ', $text);
$text = quoted_printable_decode($text);
}
$out .= rcube_charset::convert($text, $charset);
$tmp = array();
}
// add the last part of the input string
if ($start != strlen($input)) {
$out .= rcube_charset::convert(substr($input, $start), $default_charset);
}
// return the results
return $out;
}
// no encoding information, use fallback
return rcube_charset::convert($input, $default_charset);
}
/**
* Decode a mime part
*
* @param string $input Input string
* @param string $encoding Part encoding
* @return string Decoded string
*/
public static function decode($input, $encoding = '7bit')
{
switch (strtolower($encoding)) {
case 'quoted-printable':
return quoted_printable_decode($input);
case 'base64':
return base64_decode($input);
case 'x-uuencode':
case 'x-uue':
case 'uue':
case 'uuencode':
return convert_uudecode($input);
case '7bit':
default:
return $input;
}
}
/**
* Split RFC822 header string into an associative array
* @access private
*/
public static function parse_headers($headers)
{
$a_headers = array();
$headers = preg_replace('/\r?\n(\t| )+/', ' ', $headers);
$lines = explode("\n", $headers);
$c = count($lines);
for ($i=0; $i<$c; $i++) {
if ($p = strpos($lines[$i], ': ')) {
$field = strtolower(substr($lines[$i], 0, $p));
$value = trim(substr($lines[$i], $p+1));
if (!empty($value))
$a_headers[$field] = $value;
}
}
return $a_headers;
}
/**
* @access private
*/
private static function parse_address_list($str, $decode = true, $fallback = null)
{
// remove any newlines and carriage returns before
$str = preg_replace('/\r?\n(\s|\t)?/', ' ', $str);
// extract list items, remove comments
$str = self::explode_header_string(',;', $str, true);
$result = array();
// simplified regexp, supporting quoted local part
$email_rx = '(\S+|("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+"))@\S+';
foreach ($str as $key => $val) {
$name = '';
$address = '';
$val = trim($val);
if (preg_match('/(.*)<('.$email_rx.')>$/', $val, $m)) {
$address = $m[2];
$name = trim($m[1]);
}
else if (preg_match('/^('.$email_rx.')$/', $val, $m)) {
$address = $m[1];
$name = '';
}
else {
$name = $val;
}
// dequote and/or decode name
if ($name) {
if ($name[0] == '"' && $name[strlen($name)-1] == '"') {
$name = substr($name, 1, -1);
$name = stripslashes($name);
}
if ($decode) {
$name = self::decode_header($name, $fallback);
}
}
if (!$address && $name) {
$address = $name;
}
if ($address) {
$result[$key] = array('name' => $name, 'address' => $address);
}
}
return $result;
}
/**
* Explodes header (e.g. address-list) string into array of strings
* using specified separator characters with proper handling
* of quoted-strings and comments (RFC2822)
*
* @param string $separator String containing separator characters
* @param string $str Header string
* @param bool $remove_comments Enable to remove comments
*
* @return array Header items
*/
public static function explode_header_string($separator, $str, $remove_comments = false)
{
$length = strlen($str);
$result = array();
$quoted = false;
$comment = 0;
$out = '';
for ($i=0; $i<$length; $i++) {
// we're inside a quoted string
if ($quoted) {
if ($str[$i] == '"') {
$quoted = false;
}
else if ($str[$i] == "\\") {
if ($comment <= 0) {
$out .= "\\";
}
$i++;
}
}
// we are inside a comment string
else if ($comment > 0) {
if ($str[$i] == ')') {
$comment--;
}
else if ($str[$i] == '(') {
$comment++;
}
else if ($str[$i] == "\\") {
$i++;
}
continue;
}
// separator, add to result array
else if (strpos($separator, $str[$i]) !== false) {
if ($out) {
$result[] = $out;
}
$out = '';
continue;
}
// start of quoted string
else if ($str[$i] == '"') {
$quoted = true;
}
// start of comment
else if ($remove_comments && $str[$i] == '(') {
$comment++;
}
if ($comment <= 0) {
$out .= $str[$i];
}
}
if ($out && $comment <= 0) {
$result[] = $out;
}
return $result;
}
/**
* Interpret a format=flowed message body according to RFC 2646
*
* @param string $text Raw body formatted as flowed text
*
* @return string Interpreted text with unwrapped lines and stuffed space removed
*/
public static function unfold_flowed($text)
{
$text = preg_split('/\r?\n/', $text);
$last = -1;
$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 ($q == $q_level && $line
&& isset($text[$last])
&& $text[$last][strlen($text[$last])-1] == ' '
) {
$text[$last] .= $line;
unset($text[$idx]);
}
else {
$last = $idx;
}
}
else {
$q = 0;
if ($line == '-- ') {
$last = $idx;
}
else {
// remove space-stuffing
$line = preg_replace('/^\s/', '', $line);
if (isset($text[$last]) && $line
&& $text[$last] != '-- '
&& $text[$last][strlen($text[$last])-1] == ' '
) {
$text[$last] .= $line;
unset($text[$idx]);
}
else {
$text[$idx] = $line;
$last = $idx;
}
}
}
$q_level = $q;
}
return implode("\r\n", $text);
}
/**
* Wrap the given text to comply with RFC 2646
*
* @param string $text Text to wrap
* @param int $length Length
+ * @param string $charset Character encoding of $text
*
* @return string Wrapped text
*/
- public static function format_flowed($text, $length = 72)
+ public static function format_flowed($text, $length = 72, $charset=null)
{
$text = preg_split('/\r?\n/', $text);
foreach ($text as $idx => $line) {
if ($line != '-- ') {
if ($line[0] == '>' && preg_match('/^(>+)/', $line, $regs)) {
$prefix = $regs[0];
$level = strlen($prefix);
$line = rtrim(substr($line, $level));
- $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix ");
+ $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix ", false, $charset);
}
else if ($line) {
- $line = self::wordwrap(rtrim($line), $length - 2, " \r\n");
+ $line = self::wordwrap(rtrim($line), $length - 2, " \r\n", false, $charset);
// space-stuffing
$line = preg_replace('/(^|\r\n)(From| |>)/', '\\1 \\2', $line);
}
$text[$idx] = $line;
}
}
return implode("\r\n", $text);
}
/**
* Improved wordwrap function.
*
* @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
*
* @return string Text
*/
- public static function wordwrap($string, $width=75, $break="\n", $cut=false)
+ public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null)
{
- $para = explode($break, $string);
+ if ($charset)
+ mb_internal_encoding($charset);
+
+ $para = preg_split('/\r?\n/', $string);
$string = '';
while (count($para)) {
$line = array_shift($para);
if ($line[0] == '>') {
$string .= $line.$break;
continue;
}
$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);
}
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;
}
}
else {
$string .= ($len ? $break : '').$line;
if (count($list)) {
$string .= $break;
}
$len = 0;
}
}
else {
$string .= $break.$line;
$len = $l;
}
}
}
if (count($para)) {
$string .= $break;
}
}
+ if ($charset)
+ mb_internal_encoding(RCMAIL_CHARSET);
+
return $string;
}
/**
* A method to guess the mime_type of an attachment.
*
* @param string $path Path to the file.
* @param string $name File name (with suffix)
* @param string $failover Mime type supplied for failover.
* @param string $is_stream Set to True if $path contains file body
*
* @return string
* @author Till Klampaeckel <till@php.net>
* @see http://de2.php.net/manual/en/ref.fileinfo.php
* @see http://de2.php.net/mime_content_type
*/
public static function file_content_type($path, $name, $failover = 'application/octet-stream', $is_stream = false)
{
$mime_type = null;
$mime_magic = rcube::get_instance()->config->get('mime_magic');
$mime_ext = @include RCMAIL_CONFIG_DIR . '/mimetypes.php';
// use file name suffix with hard-coded mime-type map
if (is_array($mime_ext) && $name) {
if ($suffix = substr($name, strrpos($name, '.')+1)) {
$mime_type = $mime_ext[strtolower($suffix)];
}
}
// try fileinfo extension if available
if (!$mime_type && function_exists('finfo_open')) {
if ($finfo = finfo_open(FILEINFO_MIME, $mime_magic)) {
if ($is_stream)
$mime_type = finfo_buffer($finfo, $path);
else
$mime_type = finfo_file($finfo, $path);
finfo_close($finfo);
}
}
// try PHP's mime_content_type
if (!$mime_type && !$is_stream && function_exists('mime_content_type')) {
$mime_type = @mime_content_type($path);
}
// fall back to user-submitted string
if (!$mime_type) {
$mime_type = $failover;
}
else {
// Sometimes (PHP-5.3?) content-type contains charset definition,
// Remove it (#1487122) also "charset=binary" is useless
$mime_type = array_shift(preg_split('/[; ]/', $mime_type));
}
return $mime_type;
}
/**
* Detect image type of the given binary data by checking magic numbers.
*
* @param string $data Binary file content
*
* @return string Detected mime-type or jpeg as fallback
*/
public static function image_content_type($data)
{
$type = 'jpeg';
if (preg_match('/^\x89\x50\x4E\x47/', $data)) $type = 'png';
else if (preg_match('/^\x47\x49\x46\x38/', $data)) $type = 'gif';
else if (preg_match('/^\x00\x00\x01\x00/', $data)) $type = 'ico';
// else if (preg_match('/^\xFF\xD8\xFF\xE0/', $data)) $type = 'jpeg';
return 'image/' . $type;
}
}
diff --git a/program/lib/html2text.php b/program/lib/html2text.php
index dd413e0d6..34c719302 100644
--- a/program/lib/html2text.php
+++ b/program/lib/html2text.php
@@ -1,746 +1,755 @@
<?php
/*************************************************************************
* *
* class.html2text.inc *
* *
*************************************************************************
* *
* Converts HTML to formatted plain text *
* *
* Copyright (c) 2005-2007 Jon Abernathy <jon@chuggnutt.com> *
* All rights reserved. *
* *
* This script is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* The GNU General Public License can be found at *
* http://www.gnu.org/copyleft/gpl.html. *
* *
* This script 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 General Public License for more details. *
* *
* Author(s): Jon Abernathy <jon@chuggnutt.com> *
* *
* Last modified: 08/08/07 *
* *
*************************************************************************/
/**
* Takes HTML and converts it to formatted, plain text.
*
* Thanks to Alexander Krug (http://www.krugar.de/) to pointing out and
* correcting an error in the regexp search array. Fixed 7/30/03.
*
* Updated set_html() function's file reading mechanism, 9/25/03.
*
* Thanks to Joss Sanglier (http://www.dancingbear.co.uk/) for adding
* several more HTML entity codes to the $search and $replace arrays.
* Updated 11/7/03.
*
* Thanks to Darius Kasperavicius (http://www.dar.dar.lt/) for
* suggesting the addition of $allowed_tags and its supporting function
* (which I slightly modified). Updated 3/12/04.
*
* Thanks to Justin Dearing for pointing out that a replacement for the
* <TH> tag was missing, and suggesting an appropriate fix.
* Updated 8/25/04.
*
* Thanks to Mathieu Collas (http://www.myefarm.com/) for finding a
* display/formatting bug in the _build_link_list() function: email
* readers would show the left bracket and number ("[1") as part of the
* rendered email address.
* Updated 12/16/04.
*
* Thanks to Wojciech Bajon (http://histeria.pl/) for submitting code
* to handle relative links, which I hadn't considered. I modified his
* code a bit to handle normal HTTP links and MAILTO links. Also for
* suggesting three additional HTML entity codes to search for.
* Updated 03/02/05.
*
* Thanks to Jacob Chandler for pointing out another link condition
* for the _build_link_list() function: "https".
* Updated 04/06/05.
*
* Thanks to Marc Bertrand (http://www.dresdensky.com/) for
* suggesting a revision to the word wrapping functionality; if you
* specify a $width of 0 or less, word wrapping will be ignored.
* Updated 11/02/06.
*
* *** Big housecleaning updates below:
*
* Thanks to Colin Brown (http://www.sparkdriver.co.uk/) for
* suggesting the fix to handle </li> and blank lines (whitespace).
* Christian Basedau (http://www.movetheweb.de/) also suggested the
* blank lines fix.
*
* Special thanks to Marcus Bointon (http://www.synchromedia.co.uk/),
* Christian Basedau, Norbert Laposa (http://ln5.co.uk/),
* Bas van de Weijer, and Marijn van Butselaar
* for pointing out my glaring error in the <th> handling. Marcus also
* supplied a host of fixes.
*
* Thanks to Jeffrey Silverman (http://www.newtnotes.com/) for pointing
* out that extra spaces should be compressed--a problem addressed with
* Marcus Bointon's fixes but that I had not yet incorporated.
*
* Thanks to Daniel Schledermann (http://www.typoconsult.dk/) for
* suggesting a valuable fix with <a> tag handling.
*
* Thanks to Wojciech Bajon (again!) for suggesting fixes and additions,
* including the <a> tag handling that Daniel Schledermann pointed
* out but that I had not yet incorporated. I haven't (yet)
* incorporated all of Wojciech's changes, though I may at some
* future time.
*
* *** End of the housecleaning updates. Updated 08/08/07.
*
* @author Jon Abernathy <jon@chuggnutt.com>
* @version 1.0.0
* @since PHP 4.0.2
*/
class html2text
{
/**
* Contains the HTML content to convert.
*
* @var string $html
* @access public
*/
var $html;
/**
* Contains the converted, formatted text.
*
* @var string $text
* @access public
*/
var $text;
/**
* Maximum width of the formatted text, in columns.
*
* Set this value to 0 (or less) to ignore word wrapping
* and not constrain text to a fixed-width column.
*
* @var integer $width
* @access public
*/
var $width = 70;
+ /**
+ * Target character encoding for output text
+ *
+ * @var string $charset
+ * @access public
+ */
+ var $charset = 'UTF-8';
+
/**
* List of preg* regular expression patterns to search for,
* used in conjunction with $replace.
*
* @var array $search
* @access public
* @see $replace
*/
var $search = array(
"/\r/", // Non-legal carriage return
"/[\n\t]+/", // Newlines and tabs
'/<head[^>]*>.*?<\/head>/i', // <head>
'/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with
'/<style[^>]*>.*?<\/style>/i', // <style>s -- which strip_tags supposedly has problems with
'/<p[^>]*>/i', // <P>
'/<br[^>]*>/i', // <br>
'/<i[^>]*>(.*?)<\/i>/i', // <i>
'/<em[^>]*>(.*?)<\/em>/i', // <em>
'/(<ul[^>]*>|<\/ul>)/i', // <ul> and </ul>
'/(<ol[^>]*>|<\/ol>)/i', // <ol> and </ol>
'/<li[^>]*>(.*?)<\/li>/i', // <li> and </li>
'/<li[^>]*>/i', // <li>
'/<hr[^>]*>/i', // <hr>
'/<div[^>]*>/i', // <div>
'/(<table[^>]*>|<\/table>)/i', // <table> and </table>
'/(<tr[^>]*>|<\/tr>)/i', // <tr> and </tr>
'/<td[^>]*>(.*?)<\/td>/i', // <td> and </td>
);
/**
* List of pattern replacements corresponding to patterns searched.
*
* @var array $replace
* @access public
* @see $search
*/
var $replace = array(
'', // Non-legal carriage return
' ', // Newlines and tabs
'', // <head>
'', // <script>s -- which strip_tags supposedly has problems with
'', // <style>s -- which strip_tags supposedly has problems with
"\n\n", // <P>
"\n", // <br>
'_\\1_', // <i>
'_\\1_', // <em>
"\n\n", // <ul> and </ul>
"\n\n", // <ol> and </ol>
"\t* \\1\n", // <li> and </li>
"\n\t* ", // <li>
"\n-------------------------\n", // <hr>
"<div>\n", // <div>
"\n\n", // <table> and </table>
"\n", // <tr> and </tr>
"\t\t\\1\n", // <td> and </td>
);
/**
* List of preg* regular expression patterns to search for,
* used in conjunction with $ent_replace.
*
* @var array $ent_search
* @access public
* @see $ent_replace
*/
var $ent_search = array(
'/&(nbsp|#160);/i', // Non-breaking space
'/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
// Double quotes
'/&(apos|rsquo|lsquo|#8216|#8217);/i', // Single quotes
'/&gt;/i', // Greater-than
'/&lt;/i', // Less-than
'/&(copy|#169);/i', // Copyright
'/&(trade|#8482|#153);/i', // Trademark
'/&(reg|#174);/i', // Registered
'/&(mdash|#151|#8212);/i', // mdash
'/&(ndash|minus|#8211|#8722);/i', // ndash
'/&(bull|#149|#8226);/i', // Bullet
'/&(pound|#163);/i', // Pound sign
'/&(euro|#8364);/i', // Euro sign
'/&(amp|#38);/i', // Ampersand: see _converter()
'/[ ]{2,}/', // Runs of spaces, post-handling
);
/**
* List of pattern replacements corresponding to patterns searched.
*
* @var array $ent_replace
* @access public
* @see $ent_search
*/
var $ent_replace = array(
' ', // Non-breaking space
'"', // Double quotes
"'", // Single quotes
'>',
'<',
'(c)',
'(tm)',
'(R)',
'--',
'-',
'*',
'£',
'EUR', // Euro sign. € ?
'|+|amp|+|', // Ampersand: see _converter()
' ', // Runs of spaces, post-handling
);
/**
* List of preg* regular expression patterns to search for
* and replace using callback function.
*
* @var array $callback_search
* @access public
*/
var $callback_search = array(
'/<(a) [^>]*href=("|\')([^"\']+)\2[^>]*>(.*?)<\/a>/i', // <a href="">
'/<(h)[123456]( [^>]*)?>(.*?)<\/h[123456]>/i', // h1 - h6
'/<(b)( [^>]*)?>(.*?)<\/b>/i', // <b>
'/<(strong)( [^>]*)?>(.*?)<\/strong>/i', // <strong>
'/<(th)( [^>]*)?>(.*?)<\/th>/i', // <th> and </th>
);
/**
* List of preg* regular expression patterns to search for in PRE body,
* used in conjunction with $pre_replace.
*
* @var array $pre_search
* @access public
* @see $pre_replace
*/
var $pre_search = array(
"/\n/",
"/\t/",
'/ /',
'/<pre[^>]*>/',
'/<\/pre>/'
);
/**
* List of pattern replacements corresponding to patterns searched for PRE body.
*
* @var array $pre_replace
* @access public
* @see $pre_search
*/
var $pre_replace = array(
'<br>',
'&nbsp;&nbsp;&nbsp;&nbsp;',
'&nbsp;',
'',
''
);
/**
* Contains a list of HTML tags to allow in the resulting text.
*
* @var string $allowed_tags
* @access public
* @see set_allowed_tags()
*/
var $allowed_tags = '';
/**
* Contains the base URL that relative links should resolve to.
*
* @var string $url
* @access public
*/
var $url;
/**
* Indicates whether content in the $html variable has been converted yet.
*
* @var boolean $_converted
* @access private
* @see $html, $text
*/
var $_converted = false;
/**
* Contains URL addresses from links to be rendered in plain text.
*
* @var array $_link_list
* @access private
* @see _build_link_list()
*/
var $_link_list = array();
/**
* Boolean flag, true if a table of link URLs should be listed after the text.
*
* @var boolean $_do_links
* @access private
* @see html2text()
*/
var $_do_links = true;
/**
* Constructor.
*
* If the HTML source string (or file) is supplied, the class
* will instantiate with that source propagated, all that has
* to be done it to call get_text().
*
* @param string $source HTML content
* @param boolean $from_file Indicates $source is a file to pull content from
* @param boolean $do_links Indicate whether a table of link URLs is desired
* @param integer $width Maximum width of the formatted text, 0 for no limit
* @access public
* @return void
*/
- function html2text( $source = '', $from_file = false, $do_links = true, $width = 75 )
+ function html2text( $source = '', $from_file = false, $do_links = true, $width = 75, $charset = 'UTF-8' )
{
if ( !empty($source) ) {
$this->set_html($source, $from_file);
}
$this->set_base_url();
$this->_do_links = $do_links;
$this->width = $width;
+ $this->charset = $charset;
}
/**
* Loads source HTML into memory, either from $source string or a file.
*
* @param string $source HTML content
* @param boolean $from_file Indicates $source is a file to pull content from
* @access public
* @return void
*/
function set_html( $source, $from_file = false )
{
if ( $from_file && file_exists($source) ) {
$this->html = file_get_contents($source);
}
else
$this->html = $source;
$this->_converted = false;
}
/**
* Returns the text, converted from HTML.
*
* @access public
* @return string
*/
function get_text()
{
if ( !$this->_converted ) {
$this->_convert();
}
return $this->text;
}
/**
* Prints the text, converted from HTML.
*
* @access public
* @return void
*/
function print_text()
{
print $this->get_text();
}
/**
* Alias to print_text(), operates identically.
*
* @access public
* @return void
* @see print_text()
*/
function p()
{
print $this->get_text();
}
/**
* Sets the allowed HTML tags to pass through to the resulting text.
*
* Tags should be in the form "<p>", with no corresponding closing tag.
*
* @access public
* @return void
*/
function set_allowed_tags( $allowed_tags = '' )
{
if ( !empty($allowed_tags) ) {
$this->allowed_tags = $allowed_tags;
}
}
/**
* Sets a base URL to handle relative links.
*
* @access public
* @return void
*/
function set_base_url( $url = '' )
{
if ( empty($url) ) {
if ( !empty($_SERVER['HTTP_HOST']) ) {
$this->url = 'http://' . $_SERVER['HTTP_HOST'];
} else {
$this->url = '';
}
} else {
// Strip any trailing slashes for consistency (relative
// URLs may already start with a slash like "/file.html")
if ( substr($url, -1) == '/' ) {
$url = substr($url, 0, -1);
}
$this->url = $url;
}
}
/**
* Workhorse function that does actual conversion (calls _converter() method).
*
* @access private
* @return void
*/
function _convert()
{
// Variables used for building the link list
$this->_link_list = array();
$text = trim(stripslashes($this->html));
// Convert HTML to TXT
$this->_converter($text);
// Add link list
if (!empty($this->_link_list)) {
$text .= "\n\nLinks:\n------\n";
foreach ($this->_link_list as $idx => $url) {
$text .= '[' . ($idx+1) . '] ' . $url . "\n";
}
}
$this->text = $text;
$this->_converted = true;
}
/**
* Workhorse function that does actual conversion.
*
* First performs custom tag replacement specified by $search and
* $replace arrays. Then strips any remaining HTML tags, reduces whitespace
* and newlines to a readable format, and word wraps the text to
* $width characters.
*
* @param string Reference to HTML content string
*
* @access private
* @return void
*/
function _converter(&$text)
{
// Convert <BLOCKQUOTE> (before PRE!)
$this->_convert_blockquotes($text);
// Convert <PRE>
$this->_convert_pre($text);
// Run our defined tags search-and-replace
$text = preg_replace($this->search, $this->replace, $text);
// Run our defined tags search-and-replace with callback
$text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
// Strip any other HTML tags
$text = strip_tags($text, $this->allowed_tags);
// Run our defined entities/characters search-and-replace
$text = preg_replace($this->ent_search, $this->ent_replace, $text);
// Replace known html entities
- $text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
+ $text = html_entity_decode($text, ENT_QUOTES, $this->charset);
// Remove unknown/unhandled entities (this cannot be done in search-and-replace block)
$text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text);
// Convert "|+|amp|+|" into "&", need to be done after handling of unknown entities
// This properly handles situation of "&amp;quot;" in input string
$text = str_replace('|+|amp|+|', '&', $text);
// Bring down number of empty lines to 2 max
$text = preg_replace("/\n\s+\n/", "\n\n", $text);
$text = preg_replace("/[\n]{3,}/", "\n\n", $text);
// remove leading empty lines (can be produced by eg. P tag on the beginning)
$text = ltrim($text, "\n");
// Wrap the text to a readable format
// for PHP versions >= 4.0.2. Default width is 75
// If width is 0 or less, don't wrap the text.
if ( $this->width > 0 ) {
$text = wordwrap($text, $this->width);
}
}
/**
* Helper function called by preg_replace() on link replacement.
*
* Maintains an internal list of links to be displayed at the end of the
* text, with numeric indices to the original point in the text they
* appeared. Also makes an effort at identifying and handling absolute
* and relative links.
*
* @param string $link URL of the link
* @param string $display Part of the text to associate number with
* @access private
* @return string
*/
function _build_link_list( $link, $display )
{
if (!$this->_do_links || empty($link)) {
return $display;
}
// Ignored link types
if (preg_match('!^(javascript:|mailto:|#)!i', $link)) {
return $display;
}
if (preg_match('!^([a-z][a-z0-9.+-]+:)!i', $link)) {
$url = $link;
}
else {
$url = $this->url;
if (substr($link, 0, 1) != '/') {
$url .= '/';
}
$url .= "$link";
}
if (($index = array_search($url, $this->_link_list)) === false) {
$index = count($this->_link_list);
$this->_link_list[] = $url;
}
return $display . ' [' . ($index+1) . ']';
}
/**
* Helper function for PRE body conversion.
*
* @param string HTML content
* @access private
*/
function _convert_pre(&$text)
{
// get the content of PRE element
while (preg_match('/<pre[^>]*>(.*)<\/pre>/ismU', $text, $matches)) {
$this->pre_content = $matches[1];
// Run our defined tags search-and-replace with callback
$this->pre_content = preg_replace_callback($this->callback_search,
array('html2text', '_preg_callback'), $this->pre_content);
// convert the content
$this->pre_content = sprintf('<div><br>%s<br></div>',
preg_replace($this->pre_search, $this->pre_replace, $this->pre_content));
// replace the content (use callback because content can contain $0 variable)
$text = preg_replace_callback('/<pre[^>]*>.*<\/pre>/ismU',
array('html2text', '_preg_pre_callback'), $text, 1);
// free memory
$this->pre_content = '';
}
}
/**
* Helper function for BLOCKQUOTE body conversion.
*
* @param string HTML content
* @access private
*/
function _convert_blockquotes(&$text)
{
if (preg_match_all('/<\/*blockquote[^>]*>/i', $text, $matches, PREG_OFFSET_CAPTURE)) {
$level = 0;
$diff = 0;
foreach ($matches[0] as $m) {
if ($m[0][0] == '<' && $m[0][1] == '/') {
$level--;
if ($level < 0) {
$level = 0; // malformed HTML: go to next blockquote
}
else if ($level > 0) {
// skip inner blockquote
}
else {
$end = $m[1];
$len = $end - $taglen - $start;
// Get blockquote content
$body = substr($text, $start + $taglen - $diff, $len);
// Set text width
$p_width = $this->width;
if ($this->width > 0) $this->width -= 2;
// Convert blockquote content
$body = trim($body);
$this->_converter($body);
// Add citation markers and create PRE block
$body = preg_replace('/((^|\n)>*)/', '\\1> ', trim($body));
$body = '<pre>' . htmlspecialchars($body) . '</pre>';
// Re-set text width
$this->width = $p_width;
// Replace content
$text = substr($text, 0, $start - $diff)
. $body . substr($text, $end + strlen($m[0]) - $diff);
$diff = $len + $taglen + strlen($m[0]) - strlen($body);
unset($body);
}
}
else {
if ($level == 0) {
$start = $m[1];
$taglen = strlen($m[0]);
}
$level ++;
}
}
}
}
/**
* Callback function for preg_replace_callback use.
*
* @param array PREG matches
* @return string
*/
private function _preg_callback($matches)
{
switch (strtolower($matches[1])) {
case 'b':
case 'strong':
return $this->_toupper($matches[3]);
case 'th':
return $this->_toupper("\t\t". $matches[3] ."\n");
case 'h':
return $this->_toupper("\n\n". $matches[3] ."\n\n");
case 'a':
// Remove spaces in URL (#1487805)
$url = str_replace(' ', '', $matches[3]);
return $this->_build_link_list($url, $matches[4]);
}
}
/**
* Callback function for preg_replace_callback use in PRE content handler.
*
* @param array PREG matches
* @return string
*/
private function _preg_pre_callback($matches)
{
return $this->pre_content;
}
/**
* Strtoupper function with HTML tags and entities handling.
*
* @param string $str Text to convert
* @return string Converted text
*/
private function _toupper($str)
{
// string can containg HTML tags
$chunks = preg_split('/(<[^>]*>)/', $str, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
// convert toupper only the text between HTML tags
foreach ($chunks as $idx => $chunk) {
if ($chunk[0] != '<') {
$chunks[$idx] = $this->_strtoupper($chunk);
}
}
return implode($chunks);
}
/**
* Strtoupper multibyte wrapper function with HTML entities handling.
*
* @param string $str Text to convert
* @return string Converted text
*/
private function _strtoupper($str)
{
- $str = html_entity_decode($str, ENT_COMPAT, RCMAIL_CHARSET);
+ $str = html_entity_decode($str, ENT_COMPAT, $this->charset);
if (function_exists('mb_strtoupper'))
$str = mb_strtoupper($str);
else
$str = strtoupper($str);
- $str = htmlspecialchars($str, ENT_COMPAT, RCMAIL_CHARSET);
+ $str = htmlspecialchars($str, ENT_COMPAT, $this->charset);
return $str;
}
}
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 348ac9b96..9f5080d0f 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -1,826 +1,826 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/mail/sendmail.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2011, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Compose a new mail message with all headers and attachments |
| and send it using the PEAR::Net_SMTP class or with PHP mail() |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
// remove all scripts and act as called in frame
$OUTPUT->reset();
$OUTPUT->framed = TRUE;
$savedraft = !empty($_POST['_draft']) ? true : false;
$COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC);
$COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID];
/****** checks ********/
if (!isset($COMPOSE['id'])) {
raise_error(array('code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Invalid compose ID"), true, false);
$OUTPUT->show_message('internalerror', 'error');
$OUTPUT->send('iframe');
}
if (!$savedraft) {
if (empty($_POST['_to']) && empty($_POST['_cc']) && empty($_POST['_bcc'])
&& empty($_POST['_subject']) && $_POST['_message']) {
$OUTPUT->show_message('sendingfailed', 'error');
$OUTPUT->send('iframe');
}
if(!empty($CONFIG['sendmail_delay'])) {
$wait_sec = time() - intval($CONFIG['sendmail_delay']) - intval($CONFIG['last_message_time']);
if ($wait_sec < 0) {
$OUTPUT->show_message('senttooquickly', 'error', array('sec' => $wait_sec * -1));
$OUTPUT->send('iframe');
}
}
}
/****** message sending functions ********/
// encrypt parts of the header
function rcmail_encrypt_header($what)
{
global $CONFIG, $RCMAIL;
if (!$CONFIG['http_received_header_encrypt']) {
return $what;
}
return $RCMAIL->encrypt($what);
}
// get identity record
function rcmail_get_identity($id)
{
global $RCMAIL, $message_charset;
global $RCMAIL;
if ($sql_arr = $RCMAIL->user->get_identity($id)) {
$out = $sql_arr;
if ($message_charset != RCMAIL_CHARSET) {
foreach ($out as $k => $v)
$out[$k] = rcube_charset_convert($v, RCMAIL_CHARSET, $message_charset);
}
$out['mailto'] = $sql_arr['email'];
$out['string'] = format_email_recipient($sql_arr['email'], $sql_arr['name']);
return $out;
}
return FALSE;
}
/**
* go from this:
* <img src="http[s]://.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
*
* to this:
*
* <img src="/path/on/server/.../tiny_mce/plugins/emotions/images/smiley-cool.gif" border="0" alt="Cool" title="Cool" />
*/
function rcmail_fix_emoticon_paths($mime_message)
{
global $RCMAIL;
$body = $mime_message->getHTMLBody();
// remove any null-byte characters before parsing
$body = preg_replace('/\x00/', '', $body);
$searchstr = 'program/js/tiny_mce/plugins/emotions/img/';
$offset = 0;
// keep track of added images, so they're only added once
$included_images = array();
if (preg_match_all('# src=[\'"]([^\'"]+)#', $body, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[1] as $m) {
// find emoticon image tags
if (preg_match('#'.$searchstr.'(.*)$#', $m[0], $imatches)) {
$image_name = $imatches[1];
// sanitize image name so resulting attachment doesn't leave images dir
$image_name = preg_replace('/[^a-zA-Z0-9_\.\-]/i', '', $image_name);
$img_file = INSTALL_PATH . '/' . $searchstr . $image_name;
if (! in_array($image_name, $included_images)) {
// add the image to the MIME message
if (!$mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name)) {
$RCMAIL->output->show_message("emoticonerror", 'error');
}
array_push($included_images, $image_name);
}
$body = substr_replace($body, $img_file, $m[1] + $offset, strlen($m[0]));
$offset += strlen($img_file) - strlen($m[0]);
}
}
}
$mime_message->setHTMLBody($body);
}
/**
* Extract image attachments from HTML content (data URIs)
*/
function rcmail_extract_inline_images($mime_message, $from)
{
$body = $mime_message->getHTMLBody();
$offset = 0;
$list = array();
$regexp = '# src=[\'"](data:(image/[a-z]+);base64,([a-z0-9+/=\r\n]+))([\'"])#i';
// get domain for the Content-ID, must be the same as in Mail_Mime::get()
if (preg_match('#@([0-9a-zA-Z\-\.]+)#', $from, $matches)) {
$domain = $matches[1];
} else {
$domain = 'localhost';
}
if (preg_match_all($regexp, $body, $matches, PREG_OFFSET_CAPTURE)) {
foreach ($matches[1] as $idx => $m) {
$data = preg_replace('/\r\n/', '', $matches[3][$idx][0]);
$data = base64_decode($data);
if (empty($data)) {
continue;
}
$hash = md5($data) . '@' . $domain;
$mime_type = $matches[2][$idx][0];
$name = $list[$hash];
// add the image to the MIME message
if (!$name) {
$ext = preg_replace('#^[^/]+/#', '', $mime_type);
$name = substr($hash, 0, 8) . '.' . $ext;
$list[$hash] = $name;
$mime_message->addHTMLImage($data, $mime_type, $name, false, $hash);
}
$body = substr_replace($body, $name, $m[1] + $offset, strlen($m[0]));
$offset += strlen($name) - strlen($m[0]);
}
}
$mime_message->setHTMLBody($body);
}
/**
* Parse and cleanup email address input (and count addresses)
*
* @param string Address input
* @param boolean Do count recipients (saved in global $RECIPIENT_COUNT)
* @param boolean Validate addresses (errors saved in global $EMAIL_FORMAT_ERROR)
* @return string Canonical recipients string separated by comma
*/
function rcmail_email_input_format($mailto, $count=false, $check=true)
{
global $RCMAIL, $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT;
// simplified email regexp, supporting quoted local part
$email_regexp = '(\S+|("[^"]+"))@\S+';
$delim = trim($RCMAIL->config->get('recipients_separator', ','));
$regexp = array("/[,;$delim]\s*[\r\n]+/", '/[\r\n]+/', "/[,;$delim]\s*\$/m", '/;/', '/(\S{1})(<'.$email_regexp.'>)/U');
$replace = array($delim.' ', ', ', '', $delim, '\\1 \\2');
// replace new lines and strip ending ', ', make address input more valid
$mailto = trim(preg_replace($regexp, $replace, $mailto));
$result = array();
$items = rcube_explode_quoted_string($delim, $mailto);
foreach($items as $item) {
$item = trim($item);
// address in brackets without name (do nothing)
if (preg_match('/^<'.$email_regexp.'>$/', $item)) {
$item = rcube_idn_to_ascii(trim($item, '<>'));
$result[] = '<' . $item . '>';
// address without brackets and without name (add brackets)
} else if (preg_match('/^'.$email_regexp.'$/', $item)) {
$item = rcube_idn_to_ascii($item);
$result[] = '<' . $item . '>';
// address with name (handle name)
} else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) {
$address = $matches[0];
$name = trim(str_replace($address, '', $item));
if ($name[0] == '"' && $name[count($name)-1] == '"') {
$name = substr($name, 1, -1);
}
$name = stripcslashes($name);
$address = rcube_idn_to_ascii(trim($address, '<>'));
$result[] = format_email_recipient($address, $name);
$item = $address;
} else if (trim($item)) {
continue;
}
// check address format
$item = trim($item, '<>');
if ($item && $check && !check_email($item)) {
$EMAIL_FORMAT_ERROR = $item;
return;
}
}
if ($count) {
$RECIPIENT_COUNT += count($result);
}
return implode(', ', $result);
}
/****** compose message ********/
if (strlen($_POST['_draft_saveid']) > 3)
$olddraftmessageid = get_input_value('_draft_saveid', RCUBE_INPUT_POST);
$message_id = rcmail_gen_message_id();
// set default charset
$message_charset = isset($_POST['_charset']) ? $_POST['_charset'] : $OUTPUT->get_charset();
$EMAIL_FORMAT_ERROR = NULL;
$RECIPIENT_COUNT = 0;
$mailto = rcmail_email_input_format(get_input_value('_to', RCUBE_INPUT_POST, TRUE, $message_charset), true);
$mailcc = rcmail_email_input_format(get_input_value('_cc', RCUBE_INPUT_POST, TRUE, $message_charset), true);
$mailbcc = rcmail_email_input_format(get_input_value('_bcc', RCUBE_INPUT_POST, TRUE, $message_charset), true);
if ($EMAIL_FORMAT_ERROR) {
$OUTPUT->show_message('emailformaterror', 'error', array('email' => $EMAIL_FORMAT_ERROR));
$OUTPUT->send('iframe');
}
if (empty($mailto) && !empty($mailcc)) {
$mailto = $mailcc;
$mailcc = null;
}
else if (empty($mailto))
$mailto = 'undisclosed-recipients:;';
// Get sender name and address...
$from = get_input_value('_from', RCUBE_INPUT_POST, true, $message_charset);
// ... from identity...
if (is_numeric($from)) {
if (is_array($identity_arr = rcmail_get_identity($from))) {
if ($identity_arr['mailto'])
$from = $identity_arr['mailto'];
if ($identity_arr['string'])
$from_string = $identity_arr['string'];
}
else {
$from = null;
}
}
// ... if there is no identity record, this might be a custom from
else if ($from_string = rcmail_email_input_format($from)) {
if (preg_match('/(\S+@\S+)/', $from_string, $m))
$from = trim($m[1], '<>');
else
$from = null;
}
if (!$from_string && $from)
$from_string = $from;
// compose headers array
$headers = array();
// if configured, the Received headers goes to top, for good measure
if ($CONFIG['http_received_header'])
{
$nldlm = "\r\n\t";
// FROM/VIA
$http_header = 'from ';
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$host = $_SERVER['HTTP_X_FORWARDED_FOR'];
$hostname = gethostbyaddr($host);
if ($CONFIG['http_received_header_encrypt']) {
$http_header .= rcmail_encrypt_header($hostname);
if ($host != $hostname)
$http_header .= ' ('. rcmail_encrypt_header($host) . ')';
} else {
$http_header .= (($host != $hostname) ? $hostname : '[' . $host . ']');
if ($host != $hostname)
$http_header .= ' (['. $host .'])';
}
$http_header .= $nldlm . ' via ';
}
$host = $_SERVER['REMOTE_ADDR'];
$hostname = gethostbyaddr($host);
if ($CONFIG['http_received_header_encrypt']) {
$http_header .= rcmail_encrypt_header($hostname);
if ($host != $hostname)
$http_header .= ' ('. rcmail_encrypt_header($host) . ')';
} else {
$http_header .= (($host != $hostname) ? $hostname : '[' . $host . ']');
if ($host != $hostname)
$http_header .= ' (['. $host .'])';
}
// BY
$http_header .= $nldlm . 'by ' . $_SERVER['HTTP_HOST'];
// WITH
$http_header .= $nldlm . 'with HTTP (' . $_SERVER['SERVER_PROTOCOL'] .
' '.$_SERVER['REQUEST_METHOD'] . '); ' . date('r');
$http_header = wordwrap($http_header, 69, $nldlm);
$headers['Received'] = $http_header;
}
$headers['Date'] = rcmail_user_date();
$headers['From'] = rcube_charset_convert($from_string, RCMAIL_CHARSET, $message_charset);
$headers['To'] = $mailto;
// additional recipients
if (!empty($mailcc)) {
$headers['Cc'] = $mailcc;
}
if (!empty($mailbcc)) {
$headers['Bcc'] = $mailbcc;
}
if (!empty($identity_arr['bcc']) && stripos($headers['Bcc'], $identity_arr['bcc']) === false) {
$headers['Bcc'] = ($headers['Bcc'] ? $headers['Bcc'].', ' : '') . $identity_arr['bcc'];
$RECIPIENT_COUNT ++;
}
if (($max_recipients = (int) $RCMAIL->config->get('max_recipients')) > 0) {
if ($RECIPIENT_COUNT > $max_recipients) {
$OUTPUT->show_message('toomanyrecipients', 'error', array('max' => $max_recipients));
$OUTPUT->send('iframe');
}
}
// add subject
$headers['Subject'] = trim(get_input_value('_subject', RCUBE_INPUT_POST, TRUE, $message_charset));
if (!empty($identity_arr['organization'])) {
$headers['Organization'] = $identity_arr['organization'];
}
if (!empty($_POST['_replyto'])) {
$headers['Reply-To'] = rcmail_email_input_format(get_input_value('_replyto', RCUBE_INPUT_POST, TRUE, $message_charset));
}
else if (!empty($identity_arr['reply-to'])) {
$headers['Reply-To'] = rcmail_email_input_format($identity_arr['reply-to'], false, true);
}
if (!empty($headers['Reply-To'])) {
$headers['Mail-Reply-To'] = $headers['Reply-To'];
}
if (!empty($_POST['_followupto'])) {
$headers['Mail-Followup-To'] = rcmail_email_input_format(get_input_value('_followupto', RCUBE_INPUT_POST, TRUE, $message_charset));
}
if (!empty($COMPOSE['reply_msgid'])) {
$headers['In-Reply-To'] = $COMPOSE['reply_msgid'];
}
// remember reply/forward UIDs in special headers
if (!empty($COMPOSE['reply_uid']) && $savedraft) {
$headers['X-Draft-Info'] = array('type' => 'reply', 'uid' => $COMPOSE['reply_uid']);
}
else if (!empty($COMPOSE['forward_uid']) && $savedraft) {
$headers['X-Draft-Info'] = array('type' => 'forward', 'uid' => $COMPOSE['forward_uid']);
}
if (!empty($COMPOSE['references'])) {
$headers['References'] = $COMPOSE['references'];
}
if (!empty($_POST['_priority'])) {
$priority = intval($_POST['_priority']);
$a_priorities = array(1=>'highest', 2=>'high', 4=>'low', 5=>'lowest');
if ($str_priority = $a_priorities[$priority]) {
$headers['X-Priority'] = sprintf("%d (%s)", $priority, ucfirst($str_priority));
}
}
if (!empty($_POST['_receipt'])) {
$headers['Return-Receipt-To'] = $from_string;
$headers['Disposition-Notification-To'] = $from_string;
}
// additional headers
$headers['Message-ID'] = $message_id;
$headers['X-Sender'] = $from;
if (is_array($headers['X-Draft-Info'])) {
$headers['X-Draft-Info'] = rcmail_draftinfo_encode($headers['X-Draft-Info'] + array('folder' => $COMPOSE['mailbox']));
}
if (!empty($CONFIG['useragent'])) {
$headers['User-Agent'] = $CONFIG['useragent'];
}
// exec hook for header checking and manipulation
$data = $RCMAIL->plugins->exec_hook('message_outgoing_headers', array('headers' => $headers));
// sending aborted by plugin
if ($data['abort'] && !$savedraft) {
$OUTPUT->show_message($data['message'] ? $data['message'] : 'sendingfailed');
$OUTPUT->send('iframe');
}
else
$headers = $data['headers'];
$isHtml = (bool) get_input_value('_is_html', RCUBE_INPUT_POST);
// fetch message body
$message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset);
if ($isHtml) {
$font = rcube_fontdefs($RCMAIL->config->get('default_font', 'Verdana'));
$bstyle = $font && is_string($font) ? " style='font-family: $font'" : '';
// append doctype and html/body wrappers
$message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' .
"\r\n<html><body$bstyle>\r\n" . $message_body;
}
if (!$savedraft) {
if ($isHtml) {
// remove signature's div ID
$message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body);
// add inline css for blockquotes
$bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%';
$message_body = preg_replace('/<blockquote>/',
'<blockquote type="cite" style="'.$bstyle.'">', $message_body);
}
// Check spelling before send
if ($CONFIG['spellcheck_before_send'] && $CONFIG['enable_spellcheck']
&& empty($COMPOSE['spell_checked']) && !empty($message_body)
) {
$message_body = str_replace("\r\n", "\n", $message_body);
$spellchecker = new rcube_spellchecker(get_input_value('_lang', RCUBE_INPUT_GPC));
$spell_result = $spellchecker->check($message_body, $isHtml);
$COMPOSE['spell_checked'] = true;
if (!$spell_result) {
$result = $isHtml ? $spellchecker->get_words() : $spellchecker->get_xml();
$OUTPUT->show_message('mispellingsfound', 'error');
$OUTPUT->command('spellcheck_resume', $isHtml, $result);
$OUTPUT->send('iframe');
}
}
// generic footer for all messages
if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) {
$footer = file_get_contents(realpath($CONFIG['generic_message_footer_html']));
$footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
}
else if (!empty($CONFIG['generic_message_footer'])) {
$footer = file_get_contents(realpath($CONFIG['generic_message_footer']));
$footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset);
if ($isHtml)
$footer = '<pre>'.$footer.'</pre>';
}
if ($footer)
$message_body .= "\r\n" . $footer;
}
if ($isHtml) {
$message_body .= "\r\n</body></html>\r\n";
}
// sort attachments to make sure the order is the same as in the UI (#1488423)
$files = get_input_value('_attachments', RCUBE_INPUT_POST);
if ($files) {
$files = explode(',', $files);
$files = array_flip($files);
foreach ($files as $idx => $val) {
$files[$idx] = $COMPOSE['attachments'][$idx];
unset($COMPOSE['attachments'][$idx]);
}
$COMPOSE['attachments'] = array_merge(array_filter($files), $COMPOSE['attachments']);
}
// set line length for body wrapping
$LINE_LENGTH = $RCMAIL->config->get('line_length', 72);
// Since we can handle big messages with disk usage, we need more time to work
@set_time_limit(0);
// create PEAR::Mail_mime instance
$MAIL_MIME = new Mail_mime("\r\n");
// Check if we have enough memory to handle the message in it
// It's faster than using files, so we'll do this if we only can
if (is_array($COMPOSE['attachments']) && $CONFIG['smtp_server']
&& ($mem_limit = parse_bytes(ini_get('memory_limit'))))
{
$memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
foreach ($COMPOSE['attachments'] as $id => $attachment)
$memory += $attachment['size'];
// Yeah, Net_SMTP needs up to 12x more memory, 1.33 is for base64
if ($memory * 1.33 * 12 > $mem_limit)
$MAIL_MIME->setParam('delay_file_io', true);
}
// For HTML-formatted messages, construct the MIME message with both
// the HTML part and the plain-text part
if ($isHtml) {
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $message_body, 'type' => 'html', 'message' => $MAIL_MIME));
$MAIL_MIME->setHTMLBody($plugin['body']);
// replace emoticons
$plugin['body'] = rcmail_replace_emoticons($plugin['body']);
// add a plain text version of the e-mail as an alternative part.
- $h2t = new html2text($plugin['body'], false, true, 0);
- $plainTextPart = rc_wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n");
+ $h2t = new html2text($plugin['body'], false, true, 0, $message_charset);
+ $plainTextPart = rc_wordwrap($h2t->get_text(), $LINE_LENGTH, "\r\n", false, $message_charset);
$plainTextPart = wordwrap($plainTextPart, 998, "\r\n", true);
// make sure all line endings are CRLF (#1486712)
$plainTextPart = preg_replace('/\r?\n/', "\r\n", $plainTextPart);
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $plainTextPart, 'type' => 'alternative', 'message' => $MAIL_MIME));
$MAIL_MIME->setTXTBody($plugin['body']);
// look for "emoticon" images from TinyMCE and change their src paths to
// be file paths on the server instead of URL paths.
rcmail_fix_emoticon_paths($MAIL_MIME);
// Extract image Data URIs into message attachments (#1488502)
rcmail_extract_inline_images($MAIL_MIME, $from);
}
else {
$plugin = $RCMAIL->plugins->exec_hook('message_outgoing_body',
array('body' => $message_body, 'type' => 'plain', 'message' => $MAIL_MIME));
$message_body = $plugin['body'];
// compose format=flowed content if enabled
if ($flowed = $RCMAIL->config->get('send_format_flowed', true))
- $message_body = rcube_mime::format_flowed($message_body, min($LINE_LENGTH+2, 79));
+ $message_body = rcube_mime::format_flowed($message_body, min($LINE_LENGTH+2, 79), $message_charset);
else
- $message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n");
+ $message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n", false, $message_charset);
$message_body = wordwrap($message_body, 998, "\r\n", true);
$MAIL_MIME->setTXTBody($message_body, false, true);
}
// add stored attachments, if any
if (is_array($COMPOSE['attachments']))
{
foreach ($COMPOSE['attachments'] as $id => $attachment) {
// This hook retrieves the attachment contents from the file storage backend
$attachment = $RCMAIL->plugins->exec_hook('attachment_get', $attachment);
$dispurl = '/\ssrc\s*=\s*[\'"]*\S+display-attachment\S+file=rcmfile' . preg_quote($attachment['id']) . '[\s\'"]*/';
$message_body = $MAIL_MIME->getHTMLBody();
if ($isHtml && (preg_match($dispurl, $message_body) > 0)) {
$message_body = preg_replace($dispurl, ' src="'.$attachment['name'].'" ', $message_body);
$MAIL_MIME->setHTMLBody($message_body);
if ($attachment['data'])
$MAIL_MIME->addHTMLImage($attachment['data'], $attachment['mimetype'], $attachment['name'], false);
else
$MAIL_MIME->addHTMLImage($attachment['path'], $attachment['mimetype'], $attachment['name'], true);
}
else {
$ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914
$file = $attachment['data'] ? $attachment['data'] : $attachment['path'];
// .eml attachments send inline
$MAIL_MIME->addAttachment($file,
$ctype,
$attachment['name'],
($attachment['data'] ? false : true),
($ctype == 'message/rfc822' ? '8bit' : 'base64'),
($ctype == 'message/rfc822' ? 'inline' : 'attachment'),
'', '', '',
$CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
$CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL,
'', RCMAIL_CHARSET
);
}
}
}
// choose transfer encoding for plain/text body
if (preg_match('/[^\x00-\x7F]/', $MAIL_MIME->getTXTBody()))
$transfer_encoding = $RCMAIL->config->get('force_7bit') ? 'quoted-printable' : '8bit';
else
$transfer_encoding = '7bit';
// encoding settings for mail composing
$MAIL_MIME->setParam('text_encoding', $transfer_encoding);
$MAIL_MIME->setParam('html_encoding', 'quoted-printable');
$MAIL_MIME->setParam('head_encoding', 'quoted-printable');
$MAIL_MIME->setParam('head_charset', $message_charset);
$MAIL_MIME->setParam('html_charset', $message_charset);
$MAIL_MIME->setParam('text_charset', $message_charset . ($flowed ? ";\r\n format=flowed" : ''));
// encoding subject header with mb_encode provides better results with asian characters
if (function_exists('mb_encode_mimeheader')) {
mb_internal_encoding($message_charset);
$headers['Subject'] = mb_encode_mimeheader($headers['Subject'],
$message_charset, 'Q', "\r\n", 8);
mb_internal_encoding(RCMAIL_CHARSET);
}
// pass headers to message object
$MAIL_MIME->headers($headers);
// Begin SMTP Delivery Block
if (!$savedraft)
{
// check 'From' address (identity may be incomplete)
if (empty($from)) {
$OUTPUT->show_message('nofromaddress', 'error');
$OUTPUT->send('iframe');
}
// Handle Delivery Status Notification request
if (!empty($_POST['_dsn'])) {
$smtp_opts['dsn'] = true;
}
$sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto,
$smtp_error, $mailbody_file, $smtp_opts);
// return to compose page if sending failed
if (!$sent) {
// remove temp file
if ($mailbody_file) {
unlink($mailbody_file);
}
if ($smtp_error)
$OUTPUT->show_message($smtp_error['label'], 'error', $smtp_error['vars']);
else
$OUTPUT->show_message('sendingfailed', 'error');
$OUTPUT->send('iframe');
}
// save message sent time
if (!empty($CONFIG['sendmail_delay']))
$RCMAIL->user->save_prefs(array('last_message_time' => time()));
// set replied/forwarded flag
if ($COMPOSE['reply_uid'])
$RCMAIL->storage->set_flag($COMPOSE['reply_uid'], 'ANSWERED', $COMPOSE['mailbox']);
else if ($COMPOSE['forward_uid'])
$RCMAIL->storage->set_flag($COMPOSE['forward_uid'], 'FORWARDED', $COMPOSE['mailbox']);
} // End of SMTP Delivery Block
// Determine which folder to save message
if ($savedraft)
$store_target = $CONFIG['drafts_mbox'];
else if (!$RCMAIL->config->get('no_save_sent_messages'))
$store_target = isset($_POST['_store_target']) ? get_input_value('_store_target', RCUBE_INPUT_POST) : $CONFIG['sent_mbox'];
if ($store_target) {
// check if folder is subscribed
if ($RCMAIL->storage->folder_exists($store_target, true))
$store_folder = true;
// folder may be existing but not subscribed (#1485241)
else if (!$RCMAIL->storage->folder_exists($store_target))
$store_folder = $RCMAIL->storage->create_folder($store_target, true);
else if ($RCMAIL->storage->subscribe($store_target))
$store_folder = true;
// append message to sent box
if ($store_folder) {
// message body in file
if ($mailbody_file || $MAIL_MIME->getParam('delay_file_io')) {
$headers = $MAIL_MIME->txtHeaders();
// file already created
if ($mailbody_file)
$msg = $mailbody_file;
else {
$temp_dir = $RCMAIL->config->get('temp_dir');
$mailbody_file = tempnam($temp_dir, 'rcmMsg');
if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file)))
$msg = $mailbody_file;
}
}
else {
$msg = $MAIL_MIME->getMessage();
$headers = '';
}
if (PEAR::isError($msg))
raise_error(array('code' => 650, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not create message: ".$msg->getMessage()),
TRUE, FALSE);
else {
$saved = $RCMAIL->storage->save_message($store_target, $msg, $headers,
$mailbody_file ? true : false, array('SEEN'));
}
if ($mailbody_file) {
unlink($mailbody_file);
$mailbody_file = null;
}
}
// raise error if saving failed
if (!$saved) {
raise_error(array('code' => 800, 'type' => 'imap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not save message in $store_target"), TRUE, FALSE);
if ($savedraft) {
$OUTPUT->show_message('errorsaving', 'error');
// start the auto-save timer again
$OUTPUT->command('auto_save_start');
$OUTPUT->send('iframe');
}
}
if ($olddraftmessageid) {
// delete previous saved draft
// @TODO: use message UID (remember to check UIDVALIDITY) to skip this SEARCH
$delete_idx = $RCMAIL->storage->search_once($CONFIG['drafts_mbox'],
'HEADER Message-ID '.$olddraftmessageid);
if ($del_uid = $delete_idx->get_element('FIRST')) {
$deleted = $RCMAIL->storage->delete_message($del_uid, $CONFIG['drafts_mbox']);
// raise error if deletion of old draft failed
if (!$deleted)
raise_error(array('code' => 800, 'type' => 'imap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
}
}
}
// remove temp file
else if ($mailbody_file) {
unlink($mailbody_file);
}
if ($savedraft) {
$msgid = strtr($message_id, array('>' => '', '<' => ''));
// remember new draft-uid ($saved could be an UID or TRUE here)
if (is_bool($saved)) {
$draft_idx = $RCMAIL->storage->search_once($CONFIG['drafts_mbox'], 'HEADER Message-ID '.$msgid);
$saved = $draft_idx->get_element('FIRST');
}
$COMPOSE['param']['draft_uid'] = $saved;
$plugin = $RCMAIL->plugins->exec_hook('message_draftsaved', array('msgid' => $msgid, 'uid' => $saved, 'folder' => $store_target));
// display success
$OUTPUT->show_message($plugin['message'] ? $plugin['message'] : 'messagesaved', 'confirmation');
// update "_draft_saveid" and the "cmp_hash" to prevent "Unsaved changes" warning
$OUTPUT->command('set_draft_id', $msgid);
$OUTPUT->command('compose_field_hash', true);
// start the auto-save timer again
$OUTPUT->command('auto_save_start');
$OUTPUT->send('iframe');
}
else {
rcmail_compose_cleanup($COMPOSE_ID);
if ($store_folder && !$saved)
$OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'));
else
$OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'));
$OUTPUT->send('iframe');
}
diff --git a/program/steps/utils/html2text.inc b/program/steps/utils/html2text.inc
index 67e76c1a7..e17665fec 100644
--- a/program/steps/utils/html2text.inc
+++ b/program/steps/utils/html2text.inc
@@ -1,33 +1,33 @@
<?php
/*
+-----------------------------------------------------------------------+
| program/steps/utils/html2text.inc |
| |
| This file is part of the Roundcube Webmail client |
- | Copyright (C) 2005-2010, The Roundcube Dev Team |
+ | Copyright (C) 2005-2012, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Convert HTML message to plain text |
| |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
$html = $HTTP_RAW_POST_DATA;
// Replace emoticon images with its text representation
$html = rcmail_replace_emoticons($html);
-$converter = new html2text($html);
+$converter = new html2text($html, false, true, 0);
header('Content-Type: text/plain; charset=UTF-8');
print rtrim($converter->get_text());
exit;

File Metadata

Mime Type
text/x-diff
Expires
Tue, Feb 3, 12:00 PM (1 d, 1 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
427280
Default Alt Text
(92 KB)

Event Timeline