<?php
/*
* Copyright (c) 2009-2010 ATMAIL. All rights reserved
* See http://atmail.com/license.php for license agreement
*/

require_once('Mail/RFC822.php');
require_once 'application/models/contacts.php';

class Atmail_Mail_Message extends Zend_Mail_Message
{
	public $_bodystructure;
	public $_attachments;              
	protected $_flags = null;
	private $_headersProcessed = false;

	public function __construct($args)
	{
		if( isset($args['bodystructure']) )
		{
			$this->_bodystructure = $args['bodystructure'];
		}
		parent::__construct($args); 

		//This may not be the best way to cache messages, the message class should be unaware (and not care) about what is happening higher up with the message class (caching etc)
		// should operate as an independant unit with cache happening higher up in the application heirarchy
		// as it stands now, this message class is dependant on cache dependant env vars being set up properly
		if ($this->_mail && array_key_exists('id', $args) )
		{
			//need to be server aware for testing else cache gets confused
			$authedUserData = Zend_Auth::getInstance()->getStorage()->read();
			$cacheName = Atmail_Cache::generateCacheIdString(array($this->_mail->getCurrentFolder(), $authedUserData['port'], $args['id']));
			if( !$this->_content = Atmail_Cache::fetch(Atmail_Cache::TYPE_MAIL_CONTENT, $cacheName) )
			{
				$this->_content = null;
				Atmail_Cache::store(Atmail_Cache::TYPE_MAIL_CONTENT, $cacheName, $this->_content);
			}
		}
	}

	public function getContent()
	{
		if ($this->_content !== null)
		{
			return $this->_content;
		}
        
		if ($this->_mail)
		{
			//need to be server aware for testing else cache gets confused
			$authedUserData = Zend_Auth::getInstance()->getStorage()->read();
			
			$cacheName = Atmail_Cache::generateCacheIdString(array($this->_mail->getCurrentFolder(), $authedUserData['port'], $this->_headers['uniqueid']));
			if( !$this->_content = Atmail_Cache::fetch(Atmail_Cache::TYPE_MAIL_CONTENT, $cacheName) )
			{
				$this->_content = $this->_mail->getRawContent($this->_headers['uniqueid']);
				Atmail_Cache::store(Atmail_Cache::TYPE_MAIL_CONTENT, $cacheName, $this->_content);
			}
				
			return $this->_content;
		} 
		else
		{
			throw new Atmail_Mail_Exception('no content');
		}
	}

	private function recursivelyGetAttachments( &$bodyStructureNode )
	{
		//will fetch inline, CID, and normal attachments (application layer can decide how to categorize/render/forceinline)
		$currList = array();

		if( !is_array($bodyStructureNode) )
		{
			return $currList;
		}
		
		foreach( $bodyStructureNode as $subNode )
		{
			if( isset($subNode[8]) 
			&&  is_array($subNode[8]) 
			&&  $subNode[8][0] == 'attachment' 
			&&  $subNode[8][1][0] == 'filename' )
			{
				//then we have the main kind of attachment so compile attachment record
				$currItem = array();
				$currItem['filename'] = $subNode[8][1][1];
				$currItem['sizeBytes'] = $subNode[6];
				$currItem['mimeType'] = $subNode[0] . '/' . $subNode[1];
				$currItem['encoding'] = $subNode[5];
				$currList[] = $currItem;
			} 
			else 
			{
				$currList = array_merge($this->recursivelyGetAttachments( $subNode ), $currList);
			}
		}
		return $currList;
	}

	public function getAttachmentsList()
	{
		$this->_attachments = $this->recursivelyGetAttachments( $this->_bodystructure);
		return $this->_attachments;
	}

	//TODO: consider implimenting more graceful toplines (if user enabled) + header processing to lighten remote hit
	//	(really only beneficial to remote server case = <1% usercase)
	public function processHeaders($existingDataArray = null)
	{
		
		//existingDataArray implimented because some data not available to sibling($this, like UniqueId, size (without another call to remote server) )
		// or may already be available to calling class so can use without another call to remote server
		
		if( !isset($this->_headers) )
		{
			Zend_Registry::get('log')->info('Headers not found (probably corrupted) while trying to process headers for message number ' . $this->_messageNum);
			return array(); //gracefully handle corrupted headers
		}
		$this->log = Zend_Registry::get('log');
		
		if( is_array($existingDataArray) )
		{
		
			$this->_headers = array_merge($this->_headers,$existingDataArray);
		
		}
        
		//NB Zend defines all headers as lower case unless '-' character where it is dropped and next character is Caps. wierd.
		//process headers to arrive at more human friendly headers
		
		if( !isset($this->_headers['sizeraw']) )
			$this->_headers['sizeraw'] = $this->getSize();
		$this->_headers['size'] = bytesToHumanReadable($this->_headers['sizeraw']);
		if( isset($this->_headers['date']) )
		{
			
			$this->_headers['dateraw'] = $this->_headers['date'];
			$this->_headers['date'] = $this->_headers['dateraw'];
			$this->_headers['epoch'] = strtotime($this->_headers['date']); //GMT
		
		}
		if( isset($this->_headers['preview']) )
			$this->_headers['preview'] = $this->_headers['preview'];
		
		//try flag as has attachment
		$this->_headers['hasattachments'] = 'no';
		$foundPart = null;
		if( $this->_headers['sizeraw'] > 2000 ) // to prevent huge messages from chocking processHeaders with downloadloading attachments that may not be wanted
			$this->_headers['hasattachments'] = 'yes';
		
		// USE IMAP BODYSTRUCTURE to get attachment types/list
		//need a better way of counting attachments cos currently to get a simple message list, below method will have to process all parts (slow, thick)

		//try find reply-to address
		if( isset($this->_headers['reply-to']) )
			{ /* do nothing */ }
		elseif( isset($this->_headers['from']) )
			$this->_headers['reply-to'] = $this->_headers['from'];
		elseif( isset($this->_headers['return-path']) )
			$this->_headers['reply-to'] = $this->_headers['return-path'];
		else
			$this->_headers['reply-to'] = 'unknown';
		
		//try correct from if missing
		$this->_headers['fromOrigional'] = array_key_exists('from', $this->_headers) ? $this->_headers['from'] : '';
		if( isset($this->_headers['from']) )
			{ /* do nothing */ }
		else if( isset($this->_headers['reply-to']) )
			$this->_headers['from'] = $this->_headers['reply-to'];
		else if( isset($this->_headers['return-path']) )
			$this->_headers['from'] = $this->_headers['return-path'];
		else
			$this->_headers['from'] = 'unknown';
		
		//clean up message-id if necessary
		if( isset($this->_headers['message-id']) )
			if( is_array($this->_headers['message-id']) )
				$this->_headers['message-id'] = implode($this->_headers['message-id'], ', ');

		//sometimes no header supplied
		if( !isset($this->_headers['subject']) )
			$this->_headers['subject'] = '';
		if( is_array($this->_headers['subject']) )
			$this->_headers['subject'] = $this->_headers['subject'][0]; //use first one only

		if( isset($this->_headers['to']) )
		{
			//do nothing
		}
		elseif( isset($this->_headers['envelope-to']) )
			$this->_headers['to'] = $this->_headers['envelope-to'];
		else
			$this->_headers['to'] = 'unknown';
		
		if( !isset($this->_headers['content-type']) )
			$this->_headers['content-type'] = 'text/plain'; //looks like some senders like apache dont define this in their plain text emails

		$this->_headers['priority'] = 'normal';

		//to should not be an array, some email senders violate this rule
		if( is_array($this->_headers['to']) )
			$this->_headers['to'] = implode( ", ", $this->_headers['to'] );
			
		foreach( $this->_headers as $key => &$value)
		{
			
			if ( strpos($key, 'x-priority') === 0 || strpos($key, 'x-msmail-priority') === 0 || strpos($key, 'importance') === 0)
				$this->_headers['priority'] = $value;

			if( $key!='to' && $key!='from' && $key!='cc' && messageHandling::isMimeEncoded($value) )
				$value = messageHandling::mimeEncodedDecode( $value );
			
			if( $key=='to' || $key=='from' || $key=='cc' || $key=='reply-to' )
			{

//Zend_Registry::get('log')->debug( "\n" . print_r($value, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$value \n");
				$results = getProcessedRecipientObjects($value);
//Zend_Registry::get('log')->debug( "\n" . print_r($results, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$results \n");
				if($key == 'from' )
				{
					if(!array_key_exists(0, $results))
					{
						// unknown
						$this->_headers['name'] = 'unknown';
						$this->_headers['email'] = '';
					}
					else
					{
						$this->_headers['name'] = (strlen($results[0]->personalUTF8) > 0?$results[0]->personalUTF8:'Unknown');
						$this->_headers['email'] = $results[0]->address;
						if( $this->_headers['email'] == '@' ) $this->_headers['email'] = '';
					}
				}
				$recipientsPrepared = array();
				foreach( $results as $result )
					$recipientsPrepared[] = $result->recipientPreparedUTF8;
//Zend_Registry::get('log')->debug( "\n" . print_r($recipientsPrepared, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$recipientsPrepared \n");
				
				$value = implode(', ', $recipientsPrepared);
//Zend_Registry::get('log')->debug( "\n" . print_r($value, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$value \n");
				$this->_headers[$key] = $value;
			}
			
		}
		
		// Determine if the user has a photo specified for the message view
		require_once 'application/models/contacts.php';
		$Account = Zend_Registry::get('Account'); //authorised user Account //CONSIDER: pop user photo code out because breaks IO best practice
		$this->_contacts = new contacts( array('Account' => $Account) );
		$photoEmail = trim(str_replace(array('<','>'),'',(isset($this->_headers['email'])?$this->_headers['email']:'')));
		$this->_headers['UserPhoto'] = $this->_contacts->validateUserPhoto($photoEmail, true);
		$this->_headersProcessed = true;	

		return $this->_headers;
	}

	public function getProcessedHeaders($existingDataArray = null)
	{
		if ( $this->_headersProcessed )
			return $this->_headers;
		else
			return $this->processHeaders($existingDataArray);
	}

}
