Correctly parse message/rfc822; fixed html2text conversion; code cleanup
| | |
| | | CHANGELOG RoundCube Webmail |
| | | --------------------------- |
| | | |
| | | 2007/03/19 (thomasb) |
| | | ---------- |
| | | - Don't download HTML message parts |
| | | - Convert HTML parts to plaintext if 'prefer_html' is off |
| | | - Correctly parse message/rfc822 parts (closes #1484045) |
| | | - Code cleanup |
| | | |
| | | |
| | | 2007/03/18 (thomasb) |
| | | ---------- |
| | | - Also use user_id for unique key in messages table (closes #1484074) |
| | |
| | | if (!($msg_id = $this->_uid2id($uid))) |
| | | return FALSE; |
| | | |
| | | $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); |
| | | $structure = iml_GetRawStructureArray($structure_str); |
| | | $struct = false; |
| | | |
| | | $structure_str = iil_C_FetchStructureString($this->conn, $this->mailbox, $msg_id); |
| | | $structure = iml_GetRawStructureArray($structure_str); |
| | | $struct = false; |
| | | |
| | | // parse structure and add headers |
| | | if (!empty($structure)) |
| | | { |
| | |
| | | if ($this->caching_enabled) |
| | | $this->add_message_cache($cache_key, $msg_id, $headers, $struct); |
| | | } |
| | | |
| | | return $struct; |
| | | } |
| | | |
| | | return $struct; |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | for ($i=0, $count=0; $i<count($part); $i++) |
| | | if (is_array($part[$i]) && count($part[$i]) > 5) |
| | | $struct->parts[] = $this->_structure_part($part[$i], ++$count, $struct->mime_id); |
| | | |
| | | return $struct; |
| | | |
| | | return $struct; |
| | | } |
| | | |
| | | |
| | |
| | | $struct->ctype_primary = strtolower($part[0]); |
| | | $struct->ctype_secondary = strtolower($part[1]); |
| | | $struct->mimetype = $struct->ctype_primary.'/'.$struct->ctype_secondary; |
| | | |
| | | |
| | | // read content type parameters |
| | | if (is_array($part[2])) |
| | | { |
| | | $struct->ctype_parameters = array(); |
| | | if (is_array($part[2])) |
| | | { |
| | | $struct->ctype_parameters = array(); |
| | | for ($i=0; $i<count($part[2]); $i+=2) |
| | | $struct->ctype_parameters[strtolower($part[2][$i])] = $part[2][$i+1]; |
| | | |
| | | if (isset($struct->ctype_parameters['charset'])) |
| | | $struct->charset = $struct->ctype_parameters['charset']; |
| | | } |
| | | |
| | | // read content encoding |
| | | if (!empty($part[5]) && $part[5]!='NIL') |
| | | { |
| | | $struct->encoding = strtolower($part[5]); |
| | | $struct->headers['content-transfer-encoding'] = $struct->encoding; |
| | | } |
| | | |
| | | // get part size |
| | | if (!empty($part[6]) && $part[6]!='NIL') |
| | | $struct->size = intval($part[6]); |
| | | } |
| | | |
| | | // read content encoding |
| | | if (!empty($part[5]) && $part[5]!='NIL') |
| | | { |
| | | $struct->encoding = strtolower($part[5]); |
| | | $struct->headers['content-transfer-encoding'] = $struct->encoding; |
| | | } |
| | | |
| | | // get part size |
| | | if (!empty($part[6]) && $part[6]!='NIL') |
| | | $struct->size = intval($part[6]); |
| | | |
| | | // read part disposition |
| | | // read part disposition |
| | | $di = count($part) - 2; |
| | | if ((is_array($part[$di]) && count($part[$di]) == 2 && is_array($part[$di][1])) || |
| | | (is_array($part[--$di]) && count($part[$di]) == 2)) |
| | |
| | | if (is_array($part[8][$i]) && count($part[8][$i]) > 5) |
| | | $struct->parts[] = $this->_structure_part($part[8][$i], ++$count, $struct->mime_id); |
| | | } |
| | | |
| | | // get part ID |
| | | if (!empty($part[3]) && $part[3]!='NIL') |
| | | { |
| | | $struct->content_id = $part[3]; |
| | | $struct->headers['content-id'] = $part[3]; |
| | | |
| | | if (empty($struct->disposition)) |
| | | $struct->disposition = 'inline'; |
| | | } |
| | | |
| | | // get part ID |
| | | if (!empty($part[3]) && $part[3]!='NIL') |
| | | { |
| | | $struct->content_id = $part[3]; |
| | | $struct->headers['content-id'] = $part[3]; |
| | | |
| | | if (empty($struct->disposition)) |
| | | $struct->disposition = 'inline'; |
| | | } |
| | | |
| | | // fetch message headers if message/rfc822 |
| | | if ($struct->ctype_primary=='message') |
| | | { |
| | | $headers = iil_C_FetchPartBody($this->conn, $this->mailbox, $this->_msg_id, $struct->mime_id.'.HEADER'); |
| | | $struct->headers = $this->_parse_headers($headers); |
| | | |
| | | if (is_array($part[8]) && empty($struct->parts)) |
| | | $struct->parts[] = $this->_structure_part($part[8], ++$count, $struct->mime_id); |
| | | } |
| | | |
| | | return $struct; |
| | | |
| | | // normalize filename property |
| | | if (!empty($struct->d_parameters['filename'])) |
| | | $struct->filename = $this->decode_mime_string($struct->d_parameters['filename']); |
| | | else if (!empty($struct->ctype_parameters['name'])) |
| | | $struct->filename = $this->decode_mime_string($struct->ctype_parameters['name']); |
| | | else if (!empty($struct->headers['content-description'])) |
| | | $struct->filename = $this->decode_mime_string($struct->headers['content-description']); |
| | | |
| | | return $struct; |
| | | } |
| | | |
| | | |
| | |
| | | var $ctype_secondary = 'plain'; |
| | | var $mimetype = 'text/plain'; |
| | | var $disposition = ''; |
| | | var $filename = ''; |
| | | var $encoding = '8bit'; |
| | | var $charset = ''; |
| | | var $size = 0; |
| | |
| | | "/[\n\t]+/", // Newlines and tabs |
| | | '/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with |
| | | //'/<!-- .* -->/', // Comments -- which strip_tags might have problem a with |
| | | '/<a href="([^"]+)"[^>]*>(.+?)<\/a>/ie', // <a href=""> |
| | | '/<a [^>]*href="([^"]+)"[^>]*>(.+?)<\/a>/ie', // <a href=""> |
| | | '/<h[123][^>]*>(.+?)<\/h[123]>/ie', // H1 - H3 |
| | | '/<h[456][^>]*>(.+?)<\/h[456]>/ie', // H4 - H6 |
| | | '/<p[^>]*>/i', // <P> |
| | |
| | | '', // Non-legal carriage return |
| | | ' ', // Newlines and tabs |
| | | '', // <script>s -- which strip_tags supposedly has problems with |
| | | //'', // Comments -- which strip_tags might have problem a with |
| | | //'', // Comments -- which strip_tags might have problem a with |
| | | '$this->_build_link_list("\\1", "\\2")', // <a href=""> |
| | | "strtoupper(\"\n\n\\1\n\n\")", // H1 - H3 |
| | | "ucwords(\"\n\n\\1\n\n\")", // H4 - H6 |
| | | "\n", // <P> |
| | | "ucwords(\"\n\n\\1\n\")", // H4 - H6 |
| | | "\n\n", // <P> |
| | | "\n", // <br> |
| | | 'strtoupper("\\1")', // <b> |
| | | '_\\1_', // <i> |
| | |
| | | * @access public |
| | | * @return void |
| | | */ |
| | | function html2text( $source = '', $from_file = false, $do_link_table = true ) |
| | | function html2text( $source = '', $from_file = false, $produce_link_table = true ) |
| | | { |
| | | if ( !empty($source) ) { |
| | | $this->set_html($source, $from_file); |
| | |
| | | $body = rcmail_first_text_part($MESSAGE); |
| | | $isHtml = false; |
| | | } |
| | | if (strlen($body)) |
| | | $body = rcmail_create_forward_body($body, $isHtml); |
| | | |
| | | $body = rcmail_create_forward_body($body, $isHtml); |
| | | } |
| | | else if ($compose_mode == RCUBE_COMPOSE_DRAFT) |
| | | { |
| | |
| | | } |
| | | |
| | | // add attachments |
| | | if (!isset($_SESSION['compose']['forward_attachments']) && |
| | | is_array($MESSAGE['parts']) && sizeof($MESSAGE['parts'])>1) |
| | | if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE['parts'])) |
| | | rcmail_write_compose_attachments($MESSAGE); |
| | | |
| | | |
| | | return $prefix.$body; |
| | | } |
| | | |
| | |
| | | { |
| | | if ($part->ctype_primary != 'message' && $part->ctype_primary != 'text' && |
| | | ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id'] || |
| | | (empty($part->disposition) && ($part->d_parameters['filename'] || $part->ctype_parameters['name'])))) |
| | | (empty($part->disposition) && $part->filename))) |
| | | { |
| | | $tmp_path = tempnam($temp_dir, 'rcmAttmnt'); |
| | | if ($fp = fopen($tmp_path, 'w')) |
| | |
| | | fwrite($fp, $IMAP->get_message_part($message['UID'], $pid, $part->encoding)); |
| | | fclose($fp); |
| | | |
| | | $filename = !empty($part->d_parameters['filename']) ? $part->d_parameters['filename'] : |
| | | (!empty($part->ctype_parameters['name']) ? $part->ctype_parameters['name'] : |
| | | (!empty($part->headers['content-description']) ? $part->headers['content-description'] : 'file')); |
| | | |
| | | $_SESSION['compose']['attachments'][] = array( |
| | | 'name' => rcube_imap::decode_mime_string($filename), |
| | | 'mimetype' => $part->ctype_primary . '/' . $part->ctype_secondary, |
| | | 'name' => $part->filename, |
| | | 'path' => $tmp_path |
| | | ); |
| | | } |
| | |
| | | |
| | | $body = is_array($part->replaces) ? strtr($part->body, $part->replaces) : $part->body; |
| | | |
| | | // convert html to text/plain |
| | | if ($part->ctype_secondary=='html' && $plain) |
| | | { |
| | | $txt = new html2text($body, false, true); |
| | | $body = $txt->get_text(); |
| | | $part->ctype_secondary = 'plain'; |
| | | } |
| | | |
| | | // text/html |
| | | if ($part->ctype_secondary=='html') |
| | | { |
| | |
| | | |
| | | // part is file/attachment |
| | | else if ($mail_part->disposition=='attachment' || $mail_part->disposition=='inline' || $mail_part->headers['content-id'] || |
| | | (empty($mail_part->disposition) && ($mail_part->d_parameters['filename'] || $mail_part->ctype_parameters['name']))) |
| | | (empty($mail_part->disposition) && $mail_part->filename)) |
| | | { |
| | | // skip apple resource forks |
| | | if ($message_ctype_secondary=='appledouble' && $secondary_type=='applefile') |
| | |
| | | // part belongs to a related message |
| | | if ($message_ctype_secondary=='related' && $mail_part->headers['content-id']) |
| | | { |
| | | $mail_part->filename = rcube_imap::decode_mime_string($mail_part->d_parameters['filename']); |
| | | $mail_part->content_id = preg_replace(array('/^</', '/>$/'), '', $mail_part->headers['content-id']); |
| | | $sa_inline_objects[] = $mail_part; |
| | | } |
| | | // is regular attachment |
| | | else if (($fname = $mail_part->d_parameters['filename']) || |
| | | ($fname = $mail_part->ctype_parameters['name']) || |
| | | ($fname = $mail_part->headers['content-description'])) |
| | | { |
| | | $mail_part->filename = rcube_imap::decode_mime_string($fname); |
| | | else if ($mail_part->filename) |
| | | $a_attachments[] = $mail_part; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | // message is single part non-text |
| | | else |
| | | { |
| | | if (($fname = $structure->d_parameters['filename']) || |
| | | ($fname = $structure->ctype_parameters['name']) || |
| | | ($fname = $structure->headers['content-description'])) |
| | | { |
| | | $structure->filename = rcube_imap::decode_mime_string($fname); |
| | | $a_attachments[] = $structure; |
| | | } |
| | | } |
| | | else if ($structure->filename) |
| | | $a_attachments[] = $structure; |
| | | |
| | | return array($a_return_parts, $a_attachments); |
| | | } |
| | |
| | | if (!isset($part->body)) |
| | | $part->body = $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part); |
| | | |
| | | $body = rcmail_print_body($part, $safe_mode); |
| | | $body = rcmail_print_body($part, $safe_mode, !$CONFIG['prefer_html']); |
| | | $out .= '<div class="message-part">'; |
| | | |
| | | if ($part->ctype_secondary != 'plain') |
| | | $out .= rcmail_mod_html_body($body, $attrib['id']); |
| | | $out .= rcmail_sanitize_html($body, $attrib['id']); |
| | | else |
| | | $out .= $body; |
| | | |
| | |
| | | |
| | | |
| | | // modify a HTML message that it can be displayed inside a HTML page |
| | | function rcmail_mod_html_body($body, $container_id) |
| | | function rcmail_sanitize_html($body, $container_id) |
| | | { |
| | | // remove any null-byte characters before parsing |
| | | $body = preg_replace('/\x00/', '', $body); |
| | |
| | | $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style', 'cellspacing', 'cellpadding', 'border', 'summary')); |
| | | $out = '<table '. $attrib_str . ">\n"; |
| | | |
| | | $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name']; |
| | | $filesize = $part->size; |
| | | |
| | | if ($filename) |
| | | { |
| | | $out .= sprintf('<tr><td class="title">%s</td><td>%s</td><td>[<a href="./?%s">%s</a>]</tr>'."\n", |
| | | Q(rcube_label('filename')), |
| | | Q(rcube_imap::decode_mime_string($filename)), |
| | | Q($part->filename), |
| | | str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING']), |
| | | Q(rcube_label('download'))); |
| | | } |
| | | |
| | | if ($filesize) |
| | | if ($part->size) |
| | | $out .= sprintf('<tr><td class="title">%s</td><td>%s</td></tr>'."\n", |
| | | Q(rcube_label('filesize')), |
| | | show_bytes($filesize)); |
| | | show_bytes($part->size)); |
| | | |
| | | $out .= "\n</table>"; |
| | | |
| | |
| | | { |
| | | $ctype_primary = strtolower($part->ctype_primary); |
| | | $ctype_secondary = strtolower($part->ctype_secondary); |
| | | |
| | | $mimetype = sprintf('%s/%s', $ctype_primary, $ctype_secondary); |
| | | $filename = $part->d_parameters['filename'] ? $part->d_parameters['filename'] : $part->ctype_parameters['name']; |
| | | |
| | | header("Expires: 0"); |
| | | header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); |
| | | header("Cache-Control: private", false); |
| | | header("Content-Transfer-Encoding: binary"); |
| | | header(sprintf('Content-Disposition: attachment; filename="%s";', |
| | | $filename ? rcube_imap::decode_mime_string($filename) : "roundcube.$ctype_secondary")); |
| | | |
| | | // send download headers |
| | | if ($_GET['_download']) |
| | |
| | | } |
| | | else |
| | | { |
| | | header(sprintf('Content-Disposition: %s; filename="%s";', |
| | | $part->disposition ? $part->disposition : 'attachment', |
| | | $part->filename ? $part->filename : "roundcube.$ctype_secondary")); |
| | | |
| | | // turn off output buffering and print part content |
| | | $IMAP->get_message_part($MESSAGE['UID'], $part->mime_id, $part->encoding, true); |
| | | } |
| | |
| | | width: 100%; |
| | | background-color: #EBEBEB; |
| | | table-layout: fixed; |
| | | } |
| | | |
| | | #messagebody table.headers-table |
| | | { |
| | | width: auto; |
| | | margin: 6px 8px; |
| | | background-color: #F4F4F4; |
| | | border: 1px solid #ccc; |
| | | } |
| | | |
| | | table.headers-table tr td |