From de4a3d90c74885f5be10164b5a331a5715385d77 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Mon, 15 Jun 2015 16:55:49 +0000 Subject: [PATCH] * Mail/inline image:Fix inline image handling for reply/forward mail --- .../inc/class.emailadmin_imapbase.inc.php | 29 +- mail/inc/class.mail_compose.inc.php | 37 ++- mail/inc/class.mail_ui.inc.php | 261 ++++++++---------- 3 files changed, 173 insertions(+), 154 deletions(-) diff --git a/emailadmin/inc/class.emailadmin_imapbase.inc.php b/emailadmin/inc/class.emailadmin_imapbase.inc.php index 025588d010..5e638e4ae1 100644 --- a/emailadmin/inc/class.emailadmin_imapbase.inc.php +++ b/emailadmin/inc/class.emailadmin_imapbase.inc.php @@ -5919,9 +5919,10 @@ class emailadmin_imapbase * * @param egw_mailer $_mailObject instance of the egw_mailer/phpmailer Object to be used * @param string $_html2parse the html to parse and to be altered, if conditions meet + * @param $mail_bo mail bo object * @return void */ - static function processURL2InlineImages($_mailObject, &$_html2parse) + static function processURL2InlineImages($_mailObject, &$_html2parse, $mail_bo) { //error_log(__METHOD__."()"); $imageC = 0; @@ -5957,8 +5958,32 @@ class emailadmin_imapbase $basedir = 'vfs://default'; $needTempFile = false; } + + // If it is an inline image url, we need to fetch the actuall attachment + // content and later on to be able to store its content as temp file + if (strpos($myUrl, '/index.php?menuaction=mail.mail_ui.displayImage') !== false) + { + $URI_params = array(); + // Strips the url and store it into a temp for further procss + $tmp_url = html_entity_decode($myUrl); + + parse_str(parse_url($tmp_url, PHP_URL_QUERY),$URI_params); + if ($URI_params['mailbox'] && $URI_params['uid'] && $URI_params['cid']) + { + $mail_bo->reopen(base64_decode($URI_params['mailbox'])); + $attachment = $mail_bo->getAttachmentByCID($URI_params['uid'], base64_decode($URI_params['cid']),base64_decode($URI_params['partID']),true); + $mail_bo->closeConnection(); + if ($attachment) + { + $data = $attachment->getContents(); + $mimeType = $attachment->getType(); + $filename = $attachment->getDispositionParameter('filename'); + } + } + } + if ( strlen($basedir) > 1 && substr($basedir,-1) != '/' && $myUrl[0]!='/') { $basedir .= '/'; } - if ($needTempFile) $data = file_get_contents($basedir.urldecode($myUrl)); + if ($needTempFile && !$attachment) $data = file_get_contents($basedir.urldecode($myUrl)); } if (substr($url,0,strlen('data:'))=='data:') { diff --git a/mail/inc/class.mail_compose.inc.php b/mail/inc/class.mail_compose.inc.php index 00aa5558e9..3cb4649091 100644 --- a/mail/inc/class.mail_compose.inc.php +++ b/mail/inc/class.mail_compose.inc.php @@ -1625,6 +1625,7 @@ class mail_compose #error_log( "GetDraftData (HTML) CharSet:".mb_detect_encoding($bodyParts[$i]['body'] . 'a' , strtoupper($bodyParts[$i]['charSet']).','.strtoupper($this->displayCharset).',UTF-8, ISO-8859-1')); $this->sessionData['body'] .= ($i>0?"
":""). $bodyParts[$i]['body'] ; } + $this->sessionData['body'] = mail_ui::resolve_inline_images($this->sessionData['body'], $_folder, $_uid, $_partID); } else { $this->sessionData['mimeType'] = 'plain'; @@ -1638,15 +1639,21 @@ class mail_compose #error_log( "GetDraftData (Plain) CharSet".mb_detect_encoding($bodyParts[$i]['body'] . 'a' , strtoupper($bodyParts[$i]['charSet']).','.strtoupper($this->displayCharset).',UTF-8, ISO-8859-1')); $this->sessionData['body'] .= ($i>0?"\r\n":""). $bodyParts[$i]['body'] ; } + $this->sessionData['body'] = mail_ui::resolve_inline_images($this->sessionData['body'], $_folder, $_uid, $_partID,'plain'); } - + if(($attachments = $mail_bo->getMessageAttachments($_uid,$_partID))) { foreach($attachments as $attachment) { - $this->addMessageAttachment($_uid, $attachment['partID'], - $_folder, - $attachment['name'], - $attachment['mimeType'], - $attachment['size']); + $cid = $attachment['cid']; + preg_match("/[cid:{$cid}]/", $bodyParts['0']['body'], $match); + if (!$match || !$attachment['cid']) + { + $this->addMessageAttachment($_uid, $attachment['partID'], + $_folder, + $attachment['name'], + $attachment['mimeType'], + $attachment['size']); + } } } $mail_bo->closeConnection(); @@ -1706,11 +1713,14 @@ class mail_compose if(($attachments = $mail_bo->getMessageAttachments($_uid,$_partID))) { //error_log(__METHOD__.__LINE__.':'.array2string($attachments)); foreach($attachments as $attachment) { - $this->addMessageAttachment($_uid, $attachment['partID'], - $_folder, - $attachment['name'], - $attachment['mimeType'], - $attachment['size']); + if (!($attachment['cid'] && preg_match("/image\//",$attachment['mimeType']))) + { + $this->addMessageAttachment($_uid, $attachment['partID'], + $_folder, + $attachment['name'], + $attachment['mimeType'], + $attachment['size']); + } } } } @@ -2081,6 +2091,7 @@ class mail_compose } $this->sessionData['body'] .= '
'; + $this->sessionData['body'] = mail_ui::resolve_inline_images($this->sessionData['body'], $_folder, $_uid, $_partID, 'html'); } else { //$this->sessionData['body'] = @htmlspecialchars(lang("on")." ".$headers['DATE']." ".$mail_bo->decode_header($fromAddress), ENT_QUOTES) . " ".lang("wrote").":\r\n"; // take care the way the ReplyHeader is created here, is used later on in uicompose::compose, in case you force replys to be HTML (prefs) @@ -2100,7 +2111,7 @@ class mail_compose if ($bodyParts[$i]['charSet']===false) $bodyParts[$i]['charSet'] = mail_bo::detect_encoding($bodyParts[$i]['body']); $newBody = translation::convert($bodyParts[$i]['body'], $bodyParts[$i]['charSet']); #error_log( "GetReplyData (Plain) CharSet:".mb_detect_encoding($bodyParts[$i]['body'] . 'a' , strtoupper($bodyParts[$i]['charSet']).','.strtoupper($this->displayCharset).',UTF-8, ISO-8859-1')); - + $newBody = mail_ui::resolve_inline_images($newBody, $_folder, $_uid, $_partID, 'plain'); $this->sessionData['body'] .= "\r\n"; // create body new, with good line breaks and indention foreach(explode("\n",$newBody) as $value) { @@ -2284,7 +2295,7 @@ class mail_compose $_mailObject->setBody($this->convertHTMLToText($body, true, true)); } // convert URL Images to inline images - if possible - if (!$_autosaving) mail_bo::processURL2InlineImages($_mailObject, $body); + if (!$_autosaving) mail_bo::processURL2InlineImages($_mailObject, $body, $mail_bo); if (strpos($body,"")!==false) { $body = str_replace(array('',''),'',$body); diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 427763f372..d82c5d4533 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -3004,7 +3004,7 @@ class mail_ui // create links for inline images if ($modifyURI) { - $newBody = preg_replace_callback("/\[cid:(.*)\]/iU",array($this,'image_callback_plain'),$newBody); + $newBody = self::resolve_inline_images($newBody, $this->mailbox, $this->uid, $this->partID, 'plain'); } //TODO:$newBody = $this->highlightQuotes($newBody); @@ -3055,9 +3055,7 @@ class mail_ui // create links for inline images if ($modifyURI) { - $newBody = preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",array($this,'image_callback'),$newBody); - $newBody = preg_replace_callback("/url\(cid:(.*)\);/iU",array($this,'image_callback_url'),$newBody); - $newBody = preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",array($this,'image_callback_background'),$newBody); + $newBody = self::resolve_inline_images ($newBody, $this->mailbox, $this->uid, $this->partID); } // email addresses / mailto links get now activated on client-side } @@ -3073,162 +3071,147 @@ class mail_ui return $body; } - + /** - * preg_replace callback to replace image cid url's + * Resolve inline images from CID to proper url * - * @param array $matches matches from preg_replace("/src=(\"|\')cid:(.*)(\"|\')/iU",...) - * @return string src attribute to replace + * @param string $_body message content + * @param string $_mailbox mail folder + * @param string $_uid uid + * @param string $_partID part id + * @param string $_messageType = 'html', message type is either html or plain + * @return string message body including all CID images replaced */ - function image_callback($matches) + public static function resolve_inline_images ($_body,$_mailbox, $_uid, $_partID, $_messageType = 'html') { - static $cache = array(); // some caching, if mails containing the same image multiple times - - $linkData = array ( - 'menuaction' => 'mail.mail_ui.displayImage', - 'uid' => $this->uid, - 'mailbox' => base64_encode($this->mailbox), - // as src:cid contains some kind of url, it is likely to be urlencoded - 'cid' => base64_encode(trim(urldecode($matches[2]))), - 'partID' => $this->partID, - ); - $imageURL = egw::link('/index.php', $linkData); - - // to test without data uris, comment the if close incl. it's body - if (html::$user_agent != 'msie' || html::$ua_version >= 8) + if ($_messageType === 'plain') { - if (!isset($cache[$imageURL])) - { - $attachment = $this->mail_bo->getAttachmentByCID($this->uid, trim(urldecode($matches[2])), $this->partID); - - // only use data uri for "smaller" images, as otherwise the first display of the mail takes to long - if (($attachment instanceof Horde_Mime_Part) && $attachment->getBytes() < 8192) // msie=8 allows max 32k data uris - { - $this->mail_bo->fetchPartContents($this->uid, $attachment); - $cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents()); - } - else - { - $cache[$imageURL] = $imageURL; - } - } - $imageURL = $cache[$imageURL]; + return self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, 'plain'); + } + else + { + foreach(array('src','url','background') as $type) + { + $_body = self::resolve_inline_image_byType($_body, $_mailbox, $_uid, $_partID, $type); + } + return $_body; } - return 'src="'.$imageURL.'"'; } - + /** - * preg_replace callback to replace image cid url's + * Replace CID with proper type of content understandable by browser * - * @param array $matches matches from preg_replace("/src=(\"|\')cid:(.*)(\"|\')/iU",...) - * @return string src attribute to replace + * @param type $_body content of message + * @param type $_mailbox mail box + * @param type $_uid uid + * @param type $_partID part id + * @param type $_type = 'src' type of inline image that needs to be resolved and replaced + * - types: {plain|src|url|background} + * @return string returns body content including all CID replacements */ - function image_callback_plain($matches) + public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src') { - static $cache = array(); // some caching, if mails containing the same image multiple times - //error_log(__METHOD__.__LINE__.array2string($matches)); - $linkData = array ( - 'menuaction' => 'mail.mail_ui.displayImage', - 'uid' => $this->uid, - 'mailbox' => base64_encode($this->mailbox), - 'cid' => base64_encode($matches[1]), - 'partID' => $this->partID, - ); - $imageURL = egw::link('/index.php', $linkData); - - // to test without data uris, comment the if close incl. it's body - if (html::$user_agent != 'msie' || html::$ua_version >= 8) + /** + * Callback for preg_replace_callback function + * returns matched CID replacement string based on given type + * @param array $matches + * @param string $_mailbox + * @param string $_uid + * @param string $_partID + * @param string $_type + * @return string|boolean returns the replace + */ + $replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID, $_type) { - if (!isset($cache[$imageURL])) + if (!$_type) return false; + $CID = ''; + // Build up matches according to selected type + switch ($_type) { - $attachment = $this->mail_bo->getAttachmentByCID($this->uid, $matches[1], $this->partID); + case "plain": + $CID = $matches[1]; + break; + case "src": + // as src:cid contains some kind of url, it is likely to be urlencoded + $CID = urldecode($matches[2]); + break; + case "url": + $CID = $matches[1]; + break; + case "background": + $CID = $matches[2]; + break; + } - // only use data uri for "smaller" images, as otherwise the first display of the mail takes to long - if (($attachment instanceof Horde_Mime_Part) && bytes($attachment->getBytes()) < 8192) // msie=8 allows max 32k data uris + static $cache = array(); // some caching, if mails containing the same image multiple times + + if (is_array($matches) && $CID) + { + $linkData = array ( + 'menuaction' => 'mail.mail_ui.displayImage', + 'uid' => $_uid, + 'mailbox' => base64_encode($_mailbox), + 'cid' => base64_encode($CID), + 'partID' => $_partID, + ); + $imageURL = egw::link('/index.php', $linkData); + // to test without data uris, comment the if close incl. it's body + if (html::$user_agent != 'msie' || html::$ua_version >= 8) { - $this->mail_bo->fetchPartContents($this->uid, $attachment); - $cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents()); + if (!isset($cache[$imageURL])) + { + if ($_type !="background") + { + $bo = emailadmin_imapbase::getInstance(false, self::$icServerID); + $attachment = $bo->getAttachmentByCID($_uid, $CID, $_partID); + + // only use data uri for "smaller" images, as otherwise the first display of the mail takes to long + if (($attachment instanceof Horde_Mime_Part) && $attachment->getBytes() < 8192) // msie=8 allows max 32k data uris + { + $bo->fetchPartContents($_uid, $attachment); + $cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents()); + } + else + { + $cache[$imageURL] = $imageURL; + } + } + else + { + $cache[$imageURL] = $imageURL; + } + } + $imageURL = $cache[$imageURL]; } - else + + // Decides the final result of replacement according to the type + switch ($_type) { - $cache[$imageURL] = $imageURL; + case "plain": + return ''; + case "src": + return 'src="'.$imageURL.'"'; + case "url": + return 'url('.$imageURL.');'; + case "background": + return 'background="'.$imageURL.'"'; } } - $imageURL = $cache[$imageURL]; - } - return ''; - } - - /** - * preg_replace callback to replace image cid url's - * - * @param array $matches matches from preg_replace("/src=(\"|\')cid:(.*)(\"|\')/iU",...) - * @return string src attribute to replace - */ - function image_callback_url($matches) - { - static $cache = array(); // some caching, if mails containing the same image multiple times - //error_log(__METHOD__.__LINE__.array2string($matches)); - $linkData = array ( - 'menuaction' => 'mail.mail_ui.displayImage', - 'uid' => $this->uid, - 'mailbox' => base64_encode($this->mailbox), - 'cid' => base64_encode($matches[1]), - 'partID' => $this->partID, - ); - $imageURL = egw::link('/index.php', $linkData); - - // to test without data uris, comment the if close incl. it's body - if (html::$user_agent != 'msie' || html::$ua_version >= 8) + return false; + }; + + // return new body content base on chosen type + switch($_type) { - if (!isset($cache[$imageURL])) - { - $attachment = $this->mail_bo->getAttachmentByCID($this->uid, $matches[1], $this->partID); - - // only use data uri for "smaller" images, as otherwise the first display of the mail takes to long - if (($attachment instanceof Horde_Mime_Part) && $attachment->getBytes() < 8192) // msie=8 allows max 32k data uris - { - $this->mail_bo->fetchPartContents($this->uid, $attachment); - $cache[$imageURL] = 'data:'.$attachment->getType().';base64,'.base64_encode($attachment->getContents()); - } - else - { - $cache[$imageURL] = $imageURL; - } - } - $imageURL = $cache[$imageURL]; + case"plain": + return preg_replace_callback("/\[cid:(.*)\]/iU",$replace_callback,$_body); + case "src": + return preg_replace_callback("/src=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body); + case "url": + return preg_replace_callback("/url\(cid:(.*)\);/iU",$replace_callback,$_body); + case "background": + return preg_replace_callback("/background=(\"|\')cid:(.*)(\"|\')/iU",$replace_callback,$_body); } - return 'url('.$imageURL.');'; - } - - /** - * preg_replace callback to replace image cid url's - * - * @param array $matches matches from preg_replace("/src=(\"|\')cid:(.*)(\"|\')/iU",...) - * @return string src attribute to replace - */ - function image_callback_background($matches) - { - static $cache = array(); // some caching, if mails containing the same image multiple times - $linkData = array ( - 'menuaction' => 'mail.mail_ui.displayImage', - 'uid' => $this->uid, - 'mailbox' => base64_encode($this->mailbox), - 'cid' => base64_encode($matches[2]), - 'partID' => $this->partID, - ); - $imageURL = egw::link('/index.php', $linkData); - - // to test without data uris, comment the if close incl. it's body - if (html::$user_agent != 'msie' || html::$ua_version >= 8) - { - if (!isset($cache[$imageURL])) - { - $cache[$imageURL] = $imageURL; - } - $imageURL = $cache[$imageURL]; - } - return 'background="'.$imageURL.'"'; } /**