@ -67,14 +67,16 @@ class rcube_message
$this->uid = $uid;
$this->uid = $uid;
$this->headers = $this->imap->get_headers($uid, NULL, true, true);
$this->headers = $this->imap->get_headers($uid, NULL, true, true);
$this->subject = rcube_imap::decode_mime_string($this->headers->subject, $this->headers->charset);
$this->subject = rcube_imap::decode_mime_string(
$this->headers->subject, $this->headers->charset);
list(, $this->sender) = each($this->imap->decode_address_list($this->headers->from));
list(, $this->sender) = each($this->imap->decode_address_list($this->headers->from));
$this->set_safe((intval($_GET['_safe']) || $_SESSION['safe_messages'][$uid]));
$this->set_safe((intval($_GET['_safe']) || $_SESSION['safe_messages'][$uid]));
$this->opt = array(
$this->opt = array(
'safe' => $this->is_safe,
'safe' => $this->is_safe,
'prefer_html' => $this->app->config->get('prefer_html'),
'prefer_html' => $this->app->config->get('prefer_html'),
'get_url' => rcmail_url('get', array('_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
'get_url' => rcmail_url('get', array(
'_mbox' => $this->imap->get_mailbox_name(), '_uid' => $uid))
);
);
if ($this->structure = $this->imap->get_structure($uid, $this->headers->body_structure)) {
if ($this->structure = $this->imap->get_structure($uid, $this->headers->body_structure)) {
@ -103,6 +105,7 @@ class rcube_message
return $raw ? $value : $this->imap->decode_header($value);
return $raw ? $value : $this->imap->decode_header($value);
}
}
/**
/**
* Set is_safe var and session data
* Set is_safe var and session data
*
*
@ -114,6 +117,7 @@ class rcube_message
$_SESSION['safe_messages'][$this->uid] = $this->is_safe;
$_SESSION['safe_messages'][$this->uid] = $this->is_safe;
}
}
/**
/**
* Compose a valid URL for getting a message part
* Compose a valid URL for getting a message part
*
*
@ -123,7 +127,7 @@ class rcube_message
public function get_part_url($mime_id)
public function get_part_url($mime_id)
{
{
if ($this->mime_parts[$mime_id])
if ($this->mime_parts[$mime_id])
return $this->opt['get_url'] . "& _part=" . $mime_id;
return $this->opt['get_url'] . '& _part=' . $mime_id;
else
else
return false;
return false;
}
}
@ -138,9 +142,17 @@ class rcube_message
*/
*/
public function get_part_content($mime_id, $fp=NULL)
public function get_part_content($mime_id, $fp=NULL)
{
{
if ($part = $this->mime_parts[$mime_id])
if ($part = $this->mime_parts[$mime_id]) {
// stored in message structure (winmail/inline-uuencode)
if ($part->encoding == 'stream') {
if ($fp) {
fwrite($fp, $part->body);
}
return $fp ? true : $part->body;
}
// get from IMAP
return $this->imap->get_message_part($this->uid, $mime_id, $part, NULL, $fp);
return $this->imap->get_message_part($this->uid, $mime_id, $part, NULL, $fp);
else
} else
return null;
return null;
}
}
@ -162,6 +174,7 @@ class rcube_message
return false;
return false;
}
}
/**
/**
* Return the first HTML part of this message
* Return the first HTML part of this message
*
*
@ -194,13 +207,13 @@ class rcube_message
// check all message parts
// check all message parts
foreach ($this->mime_parts as $mime_id => $part) {
foreach ($this->mime_parts as $mime_id => $part) {
$mimetype = strtolower( $part->ctype_primary . '/' . $part->ctype_secondary) ;
$mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
if ($mimetype == 'text/plain') {
if ($mimetype == 'text/plain') {
$out = $this->imap->get_message_part($this->uid, $mime_id, $part);
$out = $this->imap->get_message_part($this->uid, $mime_id, $part);
// re-format format=flowed content
// re-format format=flowed content
if ($part->ctype_secondary == "plain" & & $part->ctype_parameters['format'] == "flowed" )
if ($part->ctype_secondary == 'plain' & & $part->ctype_parameters['format'] == 'flowed' )
$out = self::unfold_flowed($out);
$out = self::unfold_flowed($out);
break;
break;
}
}
@ -255,6 +268,15 @@ class rcube_message
if ($message_ctype_primary == 'text' & & !$recursive) {
if ($message_ctype_primary == 'text' & & !$recursive) {
$structure->type = 'content';
$structure->type = 'content';
$this->parts[] = &$structure;
$this->parts[] = &$structure;
// Parse simple (plain text) message body
if ($message_ctype_secondary == 'plain')
foreach ((array)$this->uu_decode($structure) as $uupart) {
$this->mime_parts[$uupart->mime_id] = $uupart;
$this->attachments[] = $uupart;
}
// @TODO: plugin hook?
}
}
// the same for pgp signed messages
// the same for pgp signed messages
else if ($mimetype == 'application/pgp' & & !$recursive) {
else if ($mimetype == 'application/pgp' & & !$recursive) {
@ -311,9 +333,9 @@ class rcube_message
else if ($html_part !== null & & empty($this->parts)) {
else if ($html_part !== null & & empty($this->parts)) {
$c = new stdClass;
$c = new stdClass;
$c->type = 'content';
$c->type = 'content';
$c->body = rcube_label('htmlmessage');
$c->ctype_primary = 'text';
$c->ctype_primary = 'text';
$c->ctype_secondary = 'plain';
$c->ctype_secondary = 'plain';
$c->body = rcube_label('htmlmessage');
$this->parts[] = $c;
$this->parts[] = $c;
}
}
@ -337,7 +359,9 @@ class rcube_message
$p->size = strlen($p->body);
$p->size = strlen($p->body);
// maybe some plugins are able to decode this encrypted message part
// maybe some plugins are able to decode this encrypted message part
$data = $this->app->plugins->exec_hook('message_part_encrypted', array('object' => $this, 'struct' => $structure, 'part' => $p));
$data = $this->app->plugins->exec_hook('message_part_encrypted',
array('object' => $this, 'struct' => $structure, 'part' => $p));
if (is_array($data['parts'])) {
if (is_array($data['parts'])) {
$this->parts = array_merge($this->parts, $data['parts']);
$this->parts = array_merge($this->parts, $data['parts']);
}
}
@ -372,12 +396,13 @@ class rcube_message
}
}
// part text/[plain|html] OR message/delivery-status
// part text/[plain|html] OR message/delivery-status
else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') & & $mail_part->disposition != 'attachment') ||
else if ((($part_mimetype == 'text/plain' || $part_mimetype == 'text/html') & & $mail_part->disposition != 'attachment') ||
$part_mimetype == 'message/delivery-status' || $part_mimetype == 'message/disposition-notification') {
$part_mimetype == 'message/delivery-status' || $part_mimetype == 'message/disposition-notification'
) {
// add text part if it matches the prefs
// add text part if it matches the prefs
if (!$this->parse_alternative ||
if (!$this->parse_alternative ||
($secondary_type == 'html' & & $this->opt['prefer_html']) ||
($secondary_type == 'html' & & $this->opt['prefer_html']) ||
($secondary_type == 'plain' & & !$this->opt['prefer_html'])) {
($secondary_type == 'plain' & & !$this->opt['prefer_html'])
) {
$mail_part->type = 'content';
$mail_part->type = 'content';
$this->parts[] = $mail_part;
$this->parts[] = $mail_part;
}
}
@ -395,20 +420,20 @@ class rcube_message
$this->attachments[] = $mail_part;
$this->attachments[] = $mail_part;
}
}
// ignore "virtual" protocol parts
// ignore "virtual" protocol parts
else if ($primary_type == 'protocol')
else if ($primary_type == 'protocol') {
continue;
continue;
}
// part is Microsoft Outlook TNEF (winmail.dat)
// part is Microsoft Outlook TNEF (winmail.dat)
else if ($part_mimetype == 'application/ms-tnef') {
else if ($part_mimetype == 'application/ms-tnef') {
foreach ((array)$this->imap-> tnef_decode($mail_part, $structure->headers['uid'] ) as $tnef_ part) {
foreach ((array)$this->tnef_decode($mail_part) as $tpart) {
$this->mime_parts[$tnef_ part->mime_id] = $tnef_ part;
$this->mime_parts[$tpart->mime_id] = $tpart;
$this->attachments[] = $tnef_ part;
$this->attachments[] = $tpart;
}
}
}
}
// part is a file/attachment
// part is a file/attachment
else if (preg_match('/^(inline|attach)/', $mail_part->disposition) ||
else if (preg_match('/^(inline|attach)/', $mail_part->disposition) ||
$mail_part->headers['content-id'] || (empty($mail_part->disposition) & & $mail_part->filename)) {
$mail_part->headers['content-id'] || (empty($mail_part->disposition) & & $mail_part->filename)
) {
// skip apple resource forks
// skip apple resource forks
if ($message_ctype_secondary == 'appledouble' & & $secondary_type == 'applefile')
if ($message_ctype_secondary == 'appledouble' & & $secondary_type == 'applefile')
continue;
continue;
@ -457,7 +482,6 @@ class rcube_message
}
}
}
}
}
}
// message is a single part non-text
// message is a single part non-text
else if ($structure->filename) {
else if ($structure->filename) {
$this->attachments[] = $structure;
$this->attachments[] = $structure;
@ -481,6 +505,88 @@ class rcube_message
}
}
/**
* Decode a Microsoft Outlook TNEF part (winmail.dat)
*
* @param object rcube_message_part Message part to decode
*/
function tnef_decode(& $part)
{
// @TODO: attachment may be huge, hadle it via file
if (!isset($part->body))
$part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part);
require_once('lib/tnef_decoder.inc');
$parts = array();
$tnef_arr = tnef_decode($part->body);
foreach ($tnef_arr as $pid => $winatt) {
$tpart = new rcube_message_part;
$tpart->filename = trim($winatt['name']);
$tpart->encoding = 'stream';
$tpart->ctype_primary = trim(strtolower($winatt['type0']));
$tpart->ctype_secondary = trim(strtolower($winatt['type1']));
$tpart->mimetype = $tpart->ctype_primary . '/' . $tpart->ctype_secondary;
$tpart->mime_id = 'winmail.' . $part->mime_id . '.' . $pid;
$tpart->size = $winatt['size'];
$tpart->body = $winatt['stream'];
$parts[] = $tpart;
unset($tnef_arr[$pid]);
}
return $parts;
}
/**
* Parse message body for UUencoded attachments bodies
*
* @param object rcube_message_part Message part to decode
*/
function uu_decode(& $part)
{
// @TODO: messages may be huge, hadle body via file
if (!isset($part->body))
$part->body = $this->imap->get_message_part($this->uid, $part->mime_id, $part);
$parts = array();
// FIXME: line length is max.65?
$uu_regexp = '/begin [0-7]{3,4} ([^\n]+)\n(([\x21-\x7E]{0,65}\n)+)`\nend/s';
if (preg_match_all($uu_regexp, $part->body, $matches, PREG_SET_ORDER)) {
// remove attachments bodies from the message body
$part->body = preg_replace($uu_regexp, '', $part->body);
// update message content-type
$part->ctype_primary = 'multipart';
$part->ctype_secondary = 'mixed';
$part->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
// add attachments to the structure
foreach ($matches as $pid => $att) {
$uupart = new rcube_message_part;
$uupart->filename = trim($att[1]);
$uupart->encoding = 'stream';
$uupart->body = convert_uudecode($att[2]);
$uupart->size = strlen($uupart->body);
$uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid;
$ctype = rc_mime_content_type($uupart->body, $uupart->filename, 'application/octet-stream', true);
$uupart->mimetype = $ctype;
list($uupart->ctype_primary, $uupart->ctype_secondary) = explode('/', $ctype);
$parts[] = $uupart;
unset($matches[$pid]);
}
}
return $parts;
}
/**
/**
* Interpret a format=flowed message body according to RFC 2646
* Interpret a format=flowed message body according to RFC 2646
*
*
@ -495,6 +601,7 @@ class rcube_message
$text);
$text);
}
}
/**
/**
* Wrap the given text to comply with RFC 2646
* Wrap the given text to comply with RFC 2646
*/
*/
@ -514,4 +621,3 @@ class rcube_message
}
}
}
}