<?php
/**
 * EGroupware - Mail - integration interface class
 *
 * @link http://www.egroupware.org
 * @package mail
 * @author Hadi Nategh [hn@stylite.de]
 * @copyright (c) 2015-16 by Stylite AG <info-AT-stylite.de>
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
 * @version $Id:$
 */

use EGroupware\Api;
use EGroupware\Api\Link;

/**
 * Class cotains methods and functions
 * to be used to integrate mail's message into other applications
 *
 */
class mail_integration {

	/**
	 * Public functions
	 * @var type
	 */
	var $public_functions = array(
		'integrate' => true
	);

	/**
	 * Maximum number of line characters (-_+=~) allowed in a mail, to not stall the layout.
	 * Longer lines / biger number of these chars are truncated to that max. number or chars.
	 *
	 * @var int
	 */
	const MAX_LINE_CHARS = 40;

	/**
	 * Gets requested mail information and sets them as data link
	 * -Execute registered hook method from the requested app for integration
	 * -with provided content from mail:
	 *
	 * -array(	'addresses' => array (
	 *					'email'=> stirng,
	 *					'personel' => string),
	 *			'attachments' => array (
	 *					'name' => string,		// file name
	 *					'type' => string,		// mime type
	 *					'egw_data'=> string,	// hash md5 id of an stored attachment in session (attachment which is in IMAP server)
	 *											// NOTE: the attachmet either have egw_data OR tmp_name (e.g. raw mail eml file stores in tmp)
	 *					'tmp_name' => string),	// tmp dir path
	 *			'message' => string,
	 *			'date' => string,
	 *			'subject' => string,
	 *			'entry_id => string				// Id of the app entry which mail content will append to
	 *	)
	 *
	 * @param string $_to_emailAddress
	 * @param string $_subject mail subject
	 * @param string $_body mail message
	 * @param array $_attachments attachments
	 * @param string $_date
	 * @param string $_rawMail path to file with raw mail
	 * @param int $_icServerID mail profile id
	 * @throws Api\Exception\AssertionFailed
	 */
	public static function integrate ($_to_emailAddress=false,$_subject=false,$_body=false,$_attachments=false,$_date=false,$_rawMail=null,$_icServerID=null)
	{
		// App name which is called for integration
		$app = isset($GLOBALS['egw_info']['user']['apps'][$_GET['app']])? $_GET['app'] : null;

		// preset app entry id, selected by user from app_entry_dialog
		$app_entry_id = $_GET['entry_id'];

		// Set the date
		if (!$_date)
		{
			$time = time();
			$_date = Api\DateTime::server2user($time->now,'ts');
		}

		// For dealing with multiple files of the same name
		$dupe_count = $file_list = array();

		$GLOBALS['egw_info']['flags']['currentapp'] = $app;
		//error_log(__METHOD__.__LINE__.': RowID:'.$_GET['rowid'].': emailAddress:'. array2string($_to_emailAddress).' && '.$app);
		// Integrate not yet saved mail
		if (empty($_GET['rowid']) && $_to_emailAddress && $app)
		{
			$sessionLocation = 'mail';
			$mailbox = base64_decode($_GET['mailbox']);

			if (!($GLOBALS['egw_info']['user']['preferences'][$sessionLocation]['saveAsOptions']==='text_only')&&is_array($_attachments))
			{
				// initialize mail open connection requirements
				if (!isset($_icServerID)) $_icServerID =& Api\Cache::getSession($sessionLocation,'activeProfileID');
				$mo = mail_bo::getInstance(true,$_icServerID);
				$mo->openConnection();
				$messagePartId = $messageFolder = null;
				foreach ($_attachments as $attachment)
				{
					//error_log(__METHOD__.__LINE__.array2string($attachment));
					if (trim(strtoupper($attachment['type'])) == 'MESSAGE/RFC822' && !empty($attachment['uid']) && !empty($attachment['folder']))
					{
						$mo->reopen(($attachment['folder']?$attachment['folder']:$mailbox));

						// get the message itself, and attach it, as we are able to display it in egw
						// instead of fetching only the attachments attached files (as we did previously)
						$message = $mo->getMessageRawBody($attachment['uid'],$attachment['partID'],($attachment['folder']?$attachment['folder']:$mailbox));
						$headers = $mo->getMessageHeader($attachment['uid'],$attachment['partID'],true,false,($attachment['folder']?$attachment['folder']:$mailbox));
						$subject = mail_bo::adaptSubjectForImport($headers['SUBJECT']);
						$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
						$tmpfile = fopen($attachment_file,'w');
						fwrite($tmpfile,$message);
						fclose($tmpfile);
						$size = filesize($attachment_file);
						$attachments[] = array(
								'name' => trim($subject).'.eml',
								'mimeType' => 'message/rfc822',
								'type' => 'message/rfc822',
								'tmp_name' => $attachment_file,
								'size' => $size,
							);
					}
					else
					{
						if (!empty($attachment['folder']))
						{
							$is_winmail = $_GET['is_winmail'] ? $_GET['is_winmail'] : 0;
							$messageFolder = $attachment['folder'];
							$messageUid = $attachment['uid'];
							$messagePartId = $attachment['partID'];
							$mo->reopen($attachment['folder']);
							$attachmentData = $mo->getAttachment($attachment['uid'],$attachment['partID'],$is_winmail,false,false,$attachment['folder']);
							$attachment['file'] =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."_");
							$tmpfile = fopen($attachment['file'],'w');
							fwrite($tmpfile,$attachmentData['attachment']);
							fclose($tmpfile);
						}
						//make sure we search for our attached file in our configured temp_dir
						if (isset($attachment['file']) && parse_url($attachment['file'],PHP_URL_SCHEME) != 'vfs' &&
							file_exists($GLOBALS['egw_info']['server']['temp_dir'].SEP.basename($attachment['file'])))
						{
							$attachment['file'] = $GLOBALS['egw_info']['server']['temp_dir'].SEP.basename($attachment['file']);
						}
						if(in_array($attachment['name'], $file_list))
						{
							$dupe_count[$attachment['name']]++;
							$attachment['name'] = pathinfo($attachment['name'], PATHINFO_FILENAME) .
								' ('.($dupe_count[$attachment['name']] + 1).')' . '.' .
								pathinfo($attachment['name'], PATHINFO_EXTENSION);
						}
						$attachments[] = array(
							'name' => $attachment['name'],
							'mimeType' => $attachment['type'],
							'type' => $attachment['type'],
							'tmp_name' => $attachment['file'],
							'size' => $attachment['size'],
						);
						$file_list[] = $attachment['name'];
					}
				}
				if ($messageFolder && $messageUid && $messagePartId && $mo->isDraftFolder($messageFolder) && !$mo->isTemplateFolder($messageFolder))
				{
					//error_log(__METHOD__.__LINE__."#".$messageUid.'#'.$messageFolder);
					try // message may be deleted already, as it maybe done by autosave
					{
						$mo->deleteMessages(array($messageUid),$messageFolder);
					}
					catch (Api\Exception $e)
					{
						//error_log(__METHOD__.__LINE__." ". str_replace('"',"'",$e->getMessage()));
						unset($e);
					}
				}
				$mo->closeConnection();
			}
			// 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' &&
				$_rawMail && file_exists($_rawMail))
			{
				$subject = mail_bo::adaptSubjectForImport($_subject);
				$attachments[] = array(
						'name' => trim($subject).'.eml',
						'mimeType' => 'message/rfc822',
						'type' => 'message/rfc822',
						'tmp_name' => $_rawMail,
						'size' => filesize($_rawMail),
						'add_raw' => true
					);
			}

			$toaddr = array();
			foreach(array('to','cc','bcc') as $x)
			{
				if (is_array($_to_emailAddress[$x]) && !empty($_to_emailAddress[$x]))
				{
					$toaddr = array_merge($toaddr,$_to_emailAddress[$x]);
				}
			}
			$body_striped = strip_tags($_body); //we need to fix broken tags (or just stuff like "<800 USD/p" )
			$body_decoded = htmlspecialchars_decode($body_striped,ENT_QUOTES);
			$body = mail_bo::createHeaderInfoSection(array('FROM'=>$_to_emailAddress['from'],
				'TO'=>(!empty($_to_emailAddress['to'])?implode(',',$_to_emailAddress['to']):null),
				'CC'=>(!empty($_to_emailAddress['cc'])?implode(',',$_to_emailAddress['cc']):null),
				'BCC'=>(!empty($_to_emailAddress['bcc'])?implode(',',$_to_emailAddress['bcc']):null),
				'SUBJECT'=>$_subject,
				'DATE'=>mail_bo::_strtotime($_date))).$body_decoded;

			$mailcontent = array(
				'mailaddress' => implode(',',$toaddr),
				'subject' => $_subject,
				'message' => $body,
				'attachments' => $attachments,
				'date' => $_date
			);

		}
		// Integrate already saved mail with ID
		else
		{
			// Initializing mail connection requirements
			$hA = mail_ui::splitRowID($_GET['rowid']);
			$sessionLocation = $hA['app']; // THIS is part of the row ID, we may use this for validation
			// Check the mail app
			if ($sessionLocation != 'mail') throw new Api\Exception\AssertionFailed(lang('Application mail expected but got: %1',$sessionLocation));
			$uid = $hA['msgUID'];
			$mailbox = $hA['folder'];
			$icServerID = $hA['profileID'];

			if ($uid && $mailbox)
			{
				if (!isset($icServerID)) $icServerID =& Api\Cache::getSession($sessionLocation,'activeProfileID');
				$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')));
				// 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')
				{
					$message = $mo->getMessageRawBody($uid, '',$mailbox);
					$headers = $mo->getMessageHeader($uid, '',true,false,$mailbox);
					$subject = mail_bo::adaptSubjectForImport($headers['SUBJECT']);
					$attachment_file =tempnam($GLOBALS['egw_info']['server']['temp_dir'],$GLOBALS['egw_info']['flags']['currentapp']."mail_integrate");
					$tmpfile = fopen($attachment_file,'w');
					fwrite($tmpfile,$message);
					fclose($tmpfile);
					$size = filesize($attachment_file);
					$mailcontent['attachments'][] = array(
							'name' => trim($subject).'.eml',
							'mimeType' => 'message/rfc822',
							'type' => 'message/rfc822',
							'tmp_name' => $attachment_file,
							'size' => $size,
							'add_raw' => true
					);
				}
				$mailcontent['date'] = strtotime($mailcontent['headers']['DATE']);
			}
		}

		// Convert addresses to email and personal
		$addresses = imap_rfc822_parse_adrlist($mailcontent['mailaddress'],'');
		foreach ($addresses as $address)
		{
			$email = sprintf('%s@%s',trim($address->mailbox),trim($address->host));
			$data_addresses[] = array (
				'email' => $email,
				'name' => !empty($address->personal) ? $address->personal : $email
			);
		}

		// shorten long (> self::max_line_chars) lines of "line" chars (-_+=~) in mails
		$data_message = preg_replace_callback(
			'/[-_+=~\.]{'.self::MAX_LINE_CHARS.',}/m',
			function($matches) {
				return substr($matches[0],0,self::MAX_LINE_CHARS);
			},
			$mailcontent['message']
		);

		// Get attachments ready for integration as link
		if (is_array($mailcontent['attachments']))
		{
			foreach($mailcontent['attachments'] as $key => $attachment)
			{
				$data_attachments[$key] = array(
					'name' => $mailcontent['attachments'][$key]['name'],
					'type' => $mailcontent['attachments'][$key]['type'],
					'size' => $mailcontent['attachments'][$key]['size'],
					'tmp_name' => $mailcontent['attachments'][$key]['tmp_name']
				);
				if ($uid && !$mailcontent['attachments'][$key]['add_raw'])
				{
					$data_attachments[$key]['egw_data'] = Link::set_data($mailcontent['attachments'][$key]['mimeType'],
						'EGroupware\\Api\\Mail::getAttachmentAccount',array($icServerID, $mailbox, $uid, $attachment['partID'], $is_winmail, true),true);
				}
				unset($mailcontent['attachments'][$key]['add_raw']);
			}
		}

		// Check if the hook is registered
		if (Api\Hooks::exists('mail_import',$app) == 0)
		{
			// Try to register hook
			Api\Hooks::read(true);
		}

		// Get the registered hook method of requested app for integration
		$hook = Api\Hooks::single(array('location' => 'mail_import'),$app);

		// Load Api\Translation for the app since the original URL
		// is from mail integration and only loads mail Api\Translation
		Api\Translation::add_app($app);

		// Execute import mail with provided content
		ExecMethod($hook['menuaction'],array (
			'addresses' => $data_addresses,
			'attachments' => $data_attachments,
			'message' => $data_message,
			'date' => $mailcontent['date'],
			'subject' => $mailcontent['subject'],
			'entry_id' => $app_entry_id
		));
	}
}