From 357f6ae65f173eb4bef34887bea57a3217ff5d3c Mon Sep 17 00:00:00 2001
From: nathangray <nathangray.bsc+github@gmail.com>
Date: Wed, 8 Nov 2017 13:09:31 -0700
Subject: [PATCH] Mail - Also include HTML version of email in integration data
 (for apps that can handle it)

---
 api/src/Mail.php                        | 21 +++++++++--
 mail/inc/class.mail_integration.inc.php | 50 ++++++++++++++++++++++++-
 mail/inc/class.mail_ui.inc.php          | 33 +++++++++++-----
 3 files changed, 89 insertions(+), 15 deletions(-)

diff --git a/api/src/Mail.php b/api/src/Mail.php
index 040d1682b5..350e408d6c 100644
--- a/api/src/Mail.php
+++ b/api/src/Mail.php
@@ -6203,7 +6203,7 @@ class Mail
 	 * @param uid the uid of the email to be processed
 	 * @param partid the partid of the email
 	 * @param mailbox the mailbox, that holds the message
-	 * @param preserveHTML flag to pass through to getdisplayableBody
+	 * @param preserveHTML flag to pass through to getdisplayableBody, null for both text and HTML
 	 * @param addHeaderSection flag to be able to supress headersection
 	 * @param includeAttachments flag to be able to supress possible attachments
 	 * @return array/bool with 'mailaddress'=>$mailaddress,
@@ -6219,6 +6219,15 @@ class Mail
 			if (empty($headers)) return false;
 			// dont force retrieval of the textpart, let mailClass preferences decide
 			$bodyParts = $mailClass->getMessageBody($uid,($preserveHTML?'always_display':'only_if_no_text'),$partid,null,false,$mailbox);
+			if(is_null($preserveHTML))
+			{
+				$html = static::getdisplayablebody(
+						$mailClass,
+						$mailClass->getMessageBody($uid,'always_display',$partid,null,false,$mailbox),
+						true
+				);
+
+			}
 			// if we do not want HTML but there is no TextRepresentation with the message itself, try converting
 			if ( !$preserveHTML && $bodyParts[0]['mimeType']=='text/html')
 			{
@@ -6316,13 +6325,19 @@ class Mail
 				}
 				if (is_array($attachedMessages)) $attachments = array_merge($attachments,$attachedMessages);
 			}
-			return array(
+			$return = array(
 					'mailaddress'=>$mailaddress,
 					'subject'=>$subject,
 					'message'=>$message,
 					'attachments'=>$attachments,
 					'headers'=>$headers,
-					);
+			);
+			if($html)
+			{
+				$return['html_message'] = $html;
+			}
+
+			return $return;
 	}
 
 	/**
diff --git a/mail/inc/class.mail_integration.inc.php b/mail/inc/class.mail_integration.inc.php
index 979f46b122..0c82de3251 100644
--- a/mail/inc/class.mail_integration.inc.php
+++ b/mail/inc/class.mail_integration.inc.php
@@ -36,6 +36,12 @@ class mail_integration {
 	 */
 	const MAX_LINE_CHARS = 40;
 
+	/**
+	 * Used to flag inline images so they can be found & urls fixed when in their
+	 * final destination.
+	 */
+	const INLINE_PREFIX = 'mail-';
+
 	/**
 	 * Gets requested mail information and sets them as data link
 	 * -Execute registered hook method from the requested app for integration
@@ -100,7 +106,7 @@ class mail_integration {
 		// Execute import mail with provided content
 		ExecMethod($hook['menuaction'],$data);
 	}
-	
+
 	/**
 	 * Gets requested mail information and sets them as data link
 	 * -with provided content from mail:
@@ -287,7 +293,7 @@ class mail_integration {
 				$mo	= mail_bo::getInstance(true,$icServerID);
 				$mo->openConnection();
 				$mo->reopen($mailbox);
-				$mailcontent = mail_bo::get_mailcontent($mo,$uid,'',$mailbox,false,true,(!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')));
+				$mailcontent = mail_bo::get_mailcontent($mo,$uid,'',$mailbox,null,true,(!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')));
 				// this one adds the mail itself (as message/rfc822 (.eml) file) to the app as additional attachment
 				// this is done to have a simple archive functionality (ToDo: opening .eml in email module)
 				if ($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='add_raw')
@@ -350,6 +356,26 @@ class mail_integration {
 						'EGroupware\\Api\\Mail::getAttachmentAccount',array($icServerID, $mailbox, $uid, $attachment['partID'], $is_winmail, true),true);
 				}
 				unset($mailcontent['attachments'][$key]['add_raw']);
+
+				// Fix inline images
+				if($mailcontent['html_message'] && $attachment['cid'] && $data_attachments[$key]['egw_data'])
+				{
+					$link_callback = function($cid) use($data_attachments, $key)
+					{
+						return self::INLINE_PREFIX.$data_attachments[$key]['egw_data'].'" title="['.$data_attachments[$key]['name'].']';
+					};
+					foreach(array('src','url','background') as $type)
+					{
+						$mailcontent['html_message'] = mail_ui::resolve_inline_image_byType(
+								$mailcontent['html_message'],
+								$mailbox,
+								$attachment['uid'],
+								$attachment['partID'],
+								$type,
+								$link_callback
+						);
+					}
+				}
 			}
 		}
 
@@ -357,10 +383,30 @@ class mail_integration {
 			'addresses' => $data_addresses,
 			'attachments' => $data_attachments,
 			'message' => $data_message,
+			'html_message' => $mailcontent['html_message'],
 			'date' => $mailcontent['date'],
 			'subject' => $mailcontent['subject'],
 			'entry_id' => $app_entry_id
 		);
 	}
+
+	public static function fix_inline_images($app, $id, array $links, &$html)
+	{
+		$replace = array();
+		foreach($links as $link)
+		{
+			$matches = null;
+			if (is_array($link) && $link['id']['egw_data'] && strpos($html, self::INLINE_PREFIX . $link['id']['egw_data']) !== false)
+			{
+				$replace[self::INLINE_PREFIX. $link['id']['egw_data']] =
+					Api\Egw::link(Api\Vfs::download_url(Api\Link::vfs_path($app, $id, Api\Vfs::basename($link['id']['name']))));
+			}
+		}
+		if ($replace)
+		{
+			$html = strtr($old = $html, $replace);
+		}
+		return isset($old) && $old != $html;
+	}
 }
 
diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php
index 97f03765fb..8020350eb4 100644
--- a/mail/inc/class.mail_ui.inc.php
+++ b/mail/inc/class.mail_ui.inc.php
@@ -3483,10 +3483,30 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
 	 * @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}
+	 * @param callback $_link_callback Function to generate the link to the image.  If
+	 *	not provided, a default (using mail) will be used.
 	 * @return string returns body content including all CID replacements
 	 */
-	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src')
+	public static function resolve_inline_image_byType ($_body,$_mailbox, $_uid, $_partID, $_type ='src', callable $_link_callback = null)
 	{
+		/**
+		 * Callback to generate the link
+		 */
+		if(is_null($_link_callback))
+		{
+			$_link_callback = function($_cid) use ($_mailbox, $_uid, $_partID)
+			{
+				$linkData = array (
+					'menuaction'    => 'mail.mail_ui.displayImage',
+					'uid'		=> $_uid,
+					'mailbox'	=> base64_encode($_mailbox),
+					'cid'		=> base64_encode($CID),
+					'partID'	=> $_partID,
+				);
+				return Egw::link('/index.php', $linkData);
+			};
+		}
+
 		/**
 		 * Callback for preg_replace_callback function
 		 * returns matched CID replacement string based on given type
@@ -3497,7 +3517,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
 		 * @param string $_type
 		 * @return string|boolean returns the replace
 		*/
-		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type)
+		$replace_callback = function ($matches) use ($_mailbox,$_uid, $_partID,  $_type, $_link_callback)
 		{
 			if (!$_type)	return false;
 			$CID = '';
@@ -3523,14 +3543,7 @@ $filter['before']= date("d-M-Y", $cutoffdate2);
 
 			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);
+				$imageURL = call_user_func($_link_callback, $CID);
 				// to test without data uris, comment the if close incl. it's body
 				if (Api\Header\UserAgent::type() != 'msie' || Api\Header\UserAgent::version() >= 8)
 				{