<?php

class Atmail_FilePreview_Plugin extends Atmail_Controller_Plugin
{
	private $_supportedFileTypes = array('pdf', 'ics', 'txt', 'html', 'tnef');
	public $_attachSeen = array();
	
	protected $_pluginFullName = 'File Preview';
	protected $_pluginAuthor = 'Brad Kowalczyk <brad@staff.atmail.com>';
	protected $_pluginDescription = 'Creates a preview thumbnail image for PDF file attachments';
	protected $_pluginCopyright = 'Copyright (c) NetBased Software Pty Ltd';
	protected $_pluginUrl = '';
	protected $_pluginCompat = '6.1.6';
	protected $_pluginNotes = 'Requires the ImageMagick package of image manipulation tools to function; Requires tnef installed to extract winmail.dat messages.';
    protected $_pluginVersion = '1.1.0';
    protected $_pluginModule = 'mail';

	private $_calendar = null;
    
    
    public function __construct()
    {
        // Set PHP extension deps
        //$this->_setDependencies(array('gd'));

		$this->log = Zend_Registry::get('log');
		
        $this->_loadConfig();
		
		parent::__construct();
	}

	public function createFilePreview($args)
	{
		if (strtolower($args['fileType']) == "tnef")
		{
			return $this->_processTnef($args);
		}
		
		if (!$this->_supportedFileType($args['fileType']))
		{
			return false;
		}
	
		$result = false;
		
		// Check the cache (currently only thumbnails are stored in the cache)
		if( $args['fileType'] != 'ics' && $data = Atmail_Cache::fetch(Atmail_Cache::TYPE_PLUGIN, $args['cacheItemId']) )
		{
			// send the attachment from cache
			header("Pragma: ");
			header("Cache-Control: ");
			header("Content-Disposition: inline; filename=\"" . $data['filename'] . "\"");
			header("Content-Type: " . strtolower($data['mimeType']));
			header("Content-Length: " . strlen($data['content']) );
			echo $data['content'];
			return true;
		}
		else if(array_key_exists('content', $args))
		{
			// no cached item, regenerate
			switch (strtolower($args['fileType']))
			{
				case 'pdf':
				{
					$data = $this->_createPdfPreview($args);
					$result = true;
				} break;
				case 'ics':
				{
					$data = $this->_createIcsPreview($args);
					$result = true;
				} break;
				default: 
				{
					return false;
				}
				break;
			}
		}
		
		if($result)
		{
			Atmail_Cache::store(Atmail_Cache::TYPE_PLUGIN, $args['cacheItemId'], $data);
		}
		return $result;
	}
	
	public function acceptFilePreview($args)
	{
		if (strtolower($args['filenameOriginal']) == "winmail.dat")
		{
			$args['fileType'] = "tnef";
		}
		
		// accepts a preview into the system
		if (!$this->_supportedFileType($args['fileType']))
		{
			return;
		}
		switch (strtolower($args['fileType']))
		{
			case 'ics':
				{
					$this->_acceptIcs($args);
				}
				break;
			default: break;
		}
	}

	public function filePreviewHtml($args)
	{
		if (strtolower($args['filenameOriginal']) == "winmail.dat")
		{
			$args['fileType'] = "tnef";
		}
		
		if (!$this->_supportedFileType($args['fileType']))
		{
			return false;
		}
		
		$requiredArgs = array('moduleBaseUrl', 'attachId', 'currentFolder', 'uniqueId', 'filenameFS', 'filenameOriginal', 'fileType', 'size');
		foreach ($requiredArgs as $v)
		{
			if (!array_key_exists($v, $args))
			{
				throw new exception('Missing argument for ' . __METHOD__);
			}
		}
		//assume currentFolder is UTF7
		$currentFolderUTF7UrlencodedDouble = urlencode(urlencode($args['currentFolder']));
		$filenameHash = md5($args['filenameOriginal']);
		$filenameOriginalHtmlentities = htmlentities($args['filenameOriginal'], ENT_QUOTES, 'UTF-8');

		//TODO: this html should really go into the template layer for more freedom.
		switch ($args['fileType'])
		{
			case 'pdf':

				$output = <<<EOF
			<li>
				<a class="ajaxDownload" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">
					<img id="pdfThumbnail" src="{$args['moduleBaseUrl']}viewmessage/getthumbnail/folder/{$currentFolderUTF7UrlencodedDouble}/thumbnail/1/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}/fileType/{$args['fileType']}" border="0">
				</a>
			</li>
			<li class="attachment pdf" id="attachment{$args['attachId']}">
				<a class="filename" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">{$filenameOriginalHtmlentities}</a> <span class="filesize">PDF Document - {$args['size']}</span>
			</li>
EOF;
				break;
			case 'ics':

				$output = <<<EOF
			<li class="calendar" id="attachment{$args['attachId']}">
				<a class="ajaxDownload" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">
					<iframe id="icsThumbnail" style="border:0; width: 100%; height:180px; overflow: hidden;" src="{$args['moduleBaseUrl']}viewmessage/getthumbnail/folder/{$currentFolderUTF7UrlencodedDouble}/thumbnail/1/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}/fileType/{$args['fileType']}" />
				</a>
				<br />
				<a class="filename" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">{$filenameOriginalHtmlentities}</a> <span class="filesize">Calendar Appointment - {$args['size']}</span>
			</li>
EOF;
				break;
			case 'txt':

				//make safe from malitious html embedded in txt part
				$txt_body = convertPlainTextLinksToUrls(strip_unsafe_tags($args['content']));
				
				$output = <<<EOF
			<li>
				<span>$txt_body</span>
			</li>
			<li class="attachment txt" id="attachment{$args['attachId']}">
				<a class="filename" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">{$filenameOriginalHtmlentities}</a> <span class="filesize">TXT Document - {$args['size']}</span>
			</li>
				
EOF;
				break;

			case 'html':

				$html_body = convertPlainTextLinksToUrls(strip_unsafe_tags($args['content']));;

				$output = <<<EOF
			<li>
				<span>$html_body</span>
			</li>
			<li class="attachment html" id="attachment{$args['attachId']}">
				<a class="filename" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">{$filenameOriginalHtmlentities}</a> <span class="filesize">HTML Document - {$args['size']}</span>
			</li>
				
EOF;
				break;
				
			case 'tnef':
				$output = <<<EOF
				
			<li class="winmail-attach" id="attachment{$args['attachId']}">
				<div id="winmail-attach-{$args['uniqueId']}" style="width: 100%;">
				</div>
				<br />
				<!--<a class="filename" href="{$args['moduleBaseUrl']}viewmessage/getattachment/folder/{$currentFolderUTF7UrlencodedDouble}/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}" target="_blank">{$filenameOriginalHtmlentities}</a> <span class="filesize">Winmail.dat attachment - {$args['size']}</span>-->
			</li>
			<script>
			\$("#winmail-attach-{$args['uniqueId']}").load("{$args['moduleBaseUrl']}viewmessage/getthumbnail/folder/{$currentFolderUTF7UrlencodedDouble}/thumbnail/1/uniqueId/{$args['uniqueId']}/filenameOriginal/{$filenameHash}/fileType/{$args['fileType']}");
			</script>
			
EOF;

		    break;

			default :
				return false;
		}
        
        return $output;
    }
    
    
    private function _processTnef($args)
    {
		if (empty($args['content']))
		{
			return false;
		}
		
		$config = Zend_Registry::get('config')->global;

		// If the path for 'tnef' is empty or not executable then try some
		// standard locations
		if (empty($config['tnefPath']) || !is_executable($config['tnefPath']))
		{
			if (is_executable("/usr/bin/tnef"))
			{
				$config['tnefPath'] = "/usr/bin/tnef";
			}
			elseif (is_executable("/usr/local/bin/tnef"))
			{
				$config['tnefPath'] = "/usr/local/bin/tnef";
			}
			else
			{
				return false;
			}
		}
		
		$tempABSFilename = tempnam(users::getTmpFolder(), 'tnef_');
		file_put_contents($tempABSFilename, $args['content']);
		
		unset($args['content']);
		// create a tmp folder for the winmail.dat contents to be extracted into
		$tmpTnef = tempnam(users::getTmpFolder(), 'tnef_');
		
		$tmpTnefFolder = $tmpTnef . '_dir';
		mkdir($tmpTnefFolder);
		unlink($tmpTnef);
		
		// extract the winmail.dat
		$command = "{$config['tnefPath']} -C " . escapeshellarg($tmpTnefFolder) . " --save-body " . escapeshellarg($tempABSFilename);
		`$command`;
		
		if (file_exists("$tmpTnefFolder/message.html"))
		{
			$html = $this->_processTnefHtmlMessage($tmpTnefFolder);
		}
		elseif (file_exists("$tmpTnefFolder/message.txt"))
		{
			$html = $this->_processTnefTextMessage($tmpTnefFolder);
		}
		elseif (file_exists("$tmpTnefFolder/message.rtf") && $unrtf = $this->_rtfConverterPath())
		{
			`$unrtf --html $tmpTnefFolder/message.rtf > $tmpTnefFolder/message.html`;
			$html = $this->_processTnefHtmlMessage($tmpTnefFolder);
		}
		else
		{
			$html = $this->_displayTnefContentsAsAttachments($tmpTnefFolder);
		}
		
		$attachments = $this->_processTnefAttachments($tmpTnefFolder);
		
		echo "<div id=\"tnef-message-{$args['extendeddata']['uniqueId']}\">$html</div>\n$attachments";
		return true;
	}
	
	
	private function _processTnefAttachments($folder)
	{
		if (!$dh = opendir($folder))
		{
			return;
		}
		
		$html = "";
		$baseUrl = $this->_getModuleBaseUrl();
		$bfolder = basename($folder);
		
		while(false !== $file = readdir($dh))
		{
			if (substr($file, 0, 1) == '.' || preg_match('/^message\.(html|rtf|txt)$/', $file))
			{
				continue;
			}
			
			$file = basename($file);
			$htmlEntFile = htmlentities($file);
			$size = filesize("$folder/$file");
		
			if (preg_match('/\.(jpg|jpeg|gif|png)$/i', $file))
			{
				$html .= <<<EOF
			<li class="image" id="attachment">
				<a class="ajaxDownload" href="$baseUrl/plugininterface/index/Atmail_FilePreview_Plugin/getImage/folder/$bfolder/name/$file" target="_blank">
					<img id="" src="$baseUrl/plugininterface/index/Atmail_FilePreview_Plugin/getImage/folder/$bfolder/name/$file" border="0">
				</a>
				<br />
				<a class="filename" href="$baseUrl/plugininterface/index/Atmail_FilePreview_Plugin/getImage/folder/$bfolder/name/$file" target="_blank">$htmlEntFile</a>
			</li>

EOF;
			}
			elseif (preg_match("/.+?\..+/", $file))
			{
				$html .= <<<EOF
			<li class="file" id="attachment">
				<a class="filename" href="$baseUrl/plugininterface/index/Atmail_FilePreview_Plugin/getFile/folder/$bfolder/name/$file" target="_blank">$htmlEntFile</a>
			</li>

EOF;
			}
		}
		
		return $html;
	}
	
	
	private function _displayTnefContentsAsAttachments($folder)
	{
		return "";
	}

	private function _rtfConverterPath()
	{
		$paths = array("/usr/bin/unrtf", "/usr/local/bin/unrtf");
		foreach ($paths as $p)
		{
			if (is_executable($p))
			{
				return $p;
			}
		}
		return false;
	}


	private function _processTnefHtmlMessage($folder)
	{
		$html = file_get_contents("$folder/message.html");
		
		$baseUrl = $this->_getModuleBaseUrl();
		
		$bfolder = basename($folder);
		
		$embeddedImages = array();
		
		if (preg_match_all("/src=(\"|')cid:((.+?)\.(jpg|jpeg|gif|png))(\"|')/i", $html, $matches))
		{
			$embeddedImages = array_combine($matches[2], $matches[0]);
		}
		
		//var_dump($matches);
		foreach ($embeddedImages as $k=>$v)
		{
			// rename embedded images so we can display links for any other images
			rename("$folder/$k", "$folder/__cid__$k");
			
			// replace the cid: notation in the html with a url that will display the image
			$html = str_replace($v, "src=\"$baseUrl/plugininterface/index/Atmail_FilePreview_Plugin/getImage/folder/$bfolder/name/__cid__$k\"", $html);
		}
		
		/*
		$html = preg_replace(
			"/src=(\"|')cid:(.+?)(\"|')/i", 
			"src=\$1$baseUrl/plugininterface/index/Atmail_FilePreview_Plugin/getImage/folder/$folder/name/\$2\$1",
			$html
		);
		*/
		
		return $html;
	}

	public function getImage($params)
	{
		$tmp = users::getTmpFolder();
		
		$folder = basename($params['folder']);
		$name = basename($params['name']);
		
		$path = "$tmp/$folder/$name";
		
		if (file_exists($path))
		{
			echo file_get_contents($path);
		}
	}
	
	public function getFile($params)
	{
		$tmp = users::getTmpFolder();
		
		$folder = basename($params['folder']);
		$name = basename($params['name']);
		
		$path = "$tmp/$folder/$name";
		
		if (file_exists($path))
		{
			header("Content-Type: application/octet-stream");
			header("Content-Disposition: attachment; filename=$name");
			header("Content-Length: " . filesize($path));
			$fh = fopen($path, 'r');
			fpassthru($fh);
		}
	}
		
	private function _processTnefTextMessage($folder)
	{
		$txt = file_get_contents("$folder/message.txt");
		
		return $txt;
	}

    private function _supportedFileType($type)
    {
        return in_array(strtolower($type), $this->_supportedFileTypes);
    }      
    
	
    private function _createPdfPreview($args)
    {
        // If the PDF file is larger than our limit then do not process
        // as 'convert' may consume too much cpu
        if (strlen($args['content']) > $this->config->max_pdf_size) {
            return false;
        }
        
		$config = Zend_Registry::get('config')->global;

		// If the path for 'convert' is empty or not executable then try some
		// standard locations
		if (empty($config['convertPath']) || !is_executable($config['convertPath']))
		{
			if (is_executable("/usr/bin/convert"))
			{
				$config['convertPath'] = "/usr/bin/convert";
			}
			elseif (is_executable("/usr/local/bin/convert"))
			{
				$config['convertPath'] = "/usr/local/bin/convert";
			}
			else
			{
				return false;
			}
		}
		$tempABSFilename = tempnam(users::getTmpFolder(), md5($args['filename']) ) . '.pdf';
		$tempABSThumbFilename = $tempABSFilename . '-thumb.gif';
		$tempThumbFilename = substr($tempABSThumbFilename, strlen(users::getTmpFolder()));
		file_put_contents($tempABSFilename, $args['content']);

		$command = $config['convertPath'] . " -thumbnail x600 '" . escapeshellcmd($tempABSFilename) . "'[0] '" . escapeshellcmd($tempABSThumbFilename) . "'";
		$output = array();
		@exec($command, $output);
		if (strpos(implode("\n", $output), 'error') !== false)
		{
			return false;
		}
		
		$file = array();
		$file['filename'] = $tempThumbFilename;
		$file['mimeType'] = "image/gif";
		$file['content'] = file_get_contents($tempABSThumbFilename);

		header("Pragma: ");
		header("Cache-Control: ");
		header("Content-Disposition: inline; filename=\"" . $file['filename'] . "\"");
		header("Content-Type: " . strtolower($file['mimeType']));
		header("Content-Length: " . strlen($file['content']));
		echo $file['content'];

		// Cleanup tmp files on disk
		unlink($tempABSFilename);
		unlink($tempABSThumbFilename);
		return $file;
	}
	
	private function _instantiateCalendar()
	{
		
		$userSettings = Zend_Registry::get('UserSettings');
		$userData = Zend_Auth::getInstance()->getStorage()->read();
		$userData = Atmail_Password::processUser($userData);
		if (empty($userSettings['CalDavUser']))
		{
			$CalDavUser = $userData['Account'];
		}
		else
		{
			$CalDavUser = $userSettings['CalDavUser'];
		}

		if (empty($userSettings['CalDavPass']))
		{
			$CalDavPass = $userData['password'];
		}
		else
		{
			$CalDavPass = $userSettings['CalDavPass'];
		}

		$this->_calendar = new calendar(array('TimeZone' => $userSettings['TimeZone'], 'Type' => $userSettings['CalDavType'], 'URL' => $userSettings['CalDavUrl'], 'Account' => $CalDavUser, 'Password' => $CalDavPass, 'Calendar' => "calendar", 'home', 'largeInstall' => '0'));
		
	}

    private function _createIcsPreview($args)
    {

        //if this is a response to ony of my own events then just show response for now (storing response in our calendar deferred till later)
        $this->_instantiateCalendar();
        $event = new Atmail_Calendar_Event();
        $event->icalendarData = trim($args['content']);
        $vCalendar = $event->getVObject();

        $uid = (string)$vCalendar->vevent->uid;
        $found = $this->_calendar->uidInMyCalendars($uid);

        $config = Zend_Registry::get('config')->global;
        if (!isset($args['extendeddata']))
            return false;
        $currentFolderUTF7UrlencodedDouble = urlencode(urlencode($args['extendeddata']['requestParams']['folder']));
        $filenameOriginalUrlencodedDouble = urlencode(urlencode($args['extendeddata']['requestParams']['filenameOriginal']));
        $uniqueId = $args['extendeddata']['requestParams']['uniqueId'];
        $fileType = $args['extendeddata']['requestParams']['fileType'];

        if( $found )
        {

            //currently we cant detect responses to our own events because other 3rd Party Calendar clients send multiple UID in response, and unhandled ATTENDEE field parameters etc.
            //upgrade to support
            //TODO: update own event with updated response fields (whose coming and not comming, tentative)
            //event found in one of my calendars so must be new from 3rd party
            echo calendar::generateHtmlPreview($vCalendar,  "{$args['extendeddata']['moduleBaseUrl']}viewmessage/getthumbnail/folder/{$currentFolderUTF7UrlencodedDouble}/thumbnail/1/uniqueId/{$uniqueId}/filenameOriginal/{$filenameOriginalUrlencodedDouble}/fileType/{$fileType}/responseStatus/", true);

        }
        else
        {

            if( isset($vCalendar->vevent) && isset($vCalendar->vevent->organizer) )
            {

                echo calendar::generateHtmlPreview($vCalendar,  "{$args['extendeddata']['moduleBaseUrl']}viewmessage/getthumbnail/folder/{$currentFolderUTF7UrlencodedDouble}/thumbnail/1/uniqueId/{$uniqueId}/filenameOriginal/{$filenameOriginalUrlencodedDouble}/fileType/{$fileType}/responseStatus/");

            }
            else
                echo calendar::generateHtmlPreview($vCalendar, null); //ical_load_entry(array('data' => $args['content'])), null);

        }
        // flag not to cache thumbnail
        return false;

    }

	private function _acceptIcs($args)
	{

		$this->_instantiateCalendar();
		$event = new Atmail_Calendar_Event();
        $event->icalendarData = trim($args['content']);
		$result = $this->_calendar->handleIncomingEventAction($args, $event);
		$baseUrl = $this->_getModuleBaseUrl();
		//depending on result, return different responses in plugin result
		//TODO: handle $response
		$siteBaseUrl = Zend_Registry::get('siteBaseUrl');
		require_once 'library/jQuery/jQuery.php';
		jQuery::evalScript("$('#action_buttons').html('Response Sent'); $('#action_buttons').attr('disabled',true).addClass('disabled').css('cursor', '').css('background','#E2F9E3 url($siteBaseUrl/images/alert-tick.gif) no-repeat scroll 0 6px').css('border','1px solid #99CC99').css('color','#006600').css('padding','4px 4px 4px 30px').css('font-size','14px'); $('#back_button', '#action_buttons').attr('onclick', '').unbind('click');");

	}
    
    public function setup()
    {
        if (!$this->_storeConfigFile(APP_ROOT . "application/modules/mail/plugins/Atmail/FilePreview/config.dist")) {
            return "The configuration file for FilePreview could not be copied 
            into Atmail's plugin configuration directory. Please create a copy 
            of the file <span style='font-family:courier'>" . 
            APP_ROOT . "application/modules/mail/plugins/Atmail/FilePreview/config.dist</span> 
            at <span style='font-family:courier'>" . APP_ROOT . "config/plugins/mail.atmail.filepreview.ini</span>";
        } else {
            return "The configuration file for FilePreview has been copied into Atmail's
            plugin configuration directory. To adjust the plugin's settings edit this 
            file: <span style='font-family:courier'>" . APP_ROOT . "config/plugins/mail.atmail.filepreview.ini</span>";
        }
    }
}
