<?php
/**
 * @category 	Atmail
 * @package 	Models
 * @subpackage	Message Handler
 * @copyright 	Copyright (c) 2009-2011 ATMAIL. All rights reserved
 * @license 	See http://atmail.com/license.php for license agreement
 * @author		Allan Wrethman allan@staff.atmail.com
 * @author		Atmail (http://atmail.com)
 */

require_once 'Atmail/Enum.php';

class messageHandling
{
	// this model contains all the Atmail specifis message handling methods for preparing the message for rendering, threads etc.
	// these methods are in the model because they were not generic enough to warrant being contributed to the library but were 
	// specific and reusable enough to warrant being seperated into their own model for use all over the app in conjunction with the message Object
	
   /**
	* TODO: place this utility function in a logical more accessible place
	* Decodes an RFC 2231 encoded string.
	*
	* @access public
	* @param string $string            The entire string to decode,optionally including the parameter name.
	* @param optional string $charset  The character set to return result in.
	* @return array  					Array of decoded info
	*/
	public static function decodeRFC2231Header($string, $charset = null)
	{
        //search for characteristic * in string and return if not found
		if (($pos = strpos($string, '*')) === false)
            return false;
        
        if ( !isset($charset) )
            $charset = 'UTF-8';

        $attribute = substr($string, 0, $pos);
        $encoded_charset = $encoded_language = null;
        $output = '';

        /* Get the characer set and language used in the encoding, if any. */
        if (preg_match("/^[^=]+\*\=([^']*)'([^']*)'/", $string, $m)) 
		{
	    	$encoded_charset = $m[1];
	        $encoded_language = $m[2];
	        $string = str_replace($encoded_charset . "'" . $encoded_language . "'", '', $string);
	    }

	    $lines = preg_split('/' . preg_quote($attribute) . '(?:\*\d)*/', $string);
	    foreach ($lines as $line) 
		{
	    	$pos = strpos($line, '*=');
	        if ($pos === 0) 
			{
	        	$line = substr($line, 2);
	            $line = str_replace('_', '%20', $line);
	            $line = str_replace('=', '%', $line);
	            $output .= urldecode($line);
	        } 
			else
			{
	        	$line = substr($line, 1);
	            $output .= $line;
	        }
	    }

	    //convert to required charset
		if( isset($encoded_charset) )
			$output = iconv($encoded_charset, $charset, $output);
	        
	    $result = array(
	            'attribute' => $attribute,
				'language' => $encoded_language, 
	            'value' => $output 
	    );
		return $result;
			
	}
	
	public static function isMimeEncoded( $string )
	{
		
		//sometimes header passed as array
		if( !is_array($string) )
			$array = array($string );
		else
			$array = $string;
			
		foreach( $array as $string )
		{
			preg_match("/=\?([^\?]+)\?([^\?])\?([^\?]+)\?=/", $string, $m);
			if( count($m) == 4 )
				return true;
		}
		return false;
		
	}

    public static function handleAttachmentsSubmitted($newMessage, $requestParams, $action = 'attach')
    {

        foreach( $requestParams['attachments'] as $attachment )
        {
            $filename = urldecode(urldecode($attachment['filenameFS']));
            $filename = makeFSSafe($filename);

            $filenameOriginal = urldecode(urldecode( $attachment['filenameOriginal']));
            switch($action)
            {
                case 'remove':
                {
                        if( file_exists( users::getTmpFolder() . $filename) && !is_dir( users::getTmpFolder() . $filename ) )
                        {
                            // Remove the attached file
                            @unlink(users::getTmpFolder() . $filename);
                        }
                }break;
                default:
                {
                    if( file_exists( users::getTmpFolder() . $filename) )
                        {
                            if( $fileContents = file_get_contents( users::getTmpFolder() . $filename ) )
                            {
                                //encode attachment filename if needed
                                $preferences = array(
                                    "input-charset" => "UTF-8",
                                    "output-charset" => "UTF-8",
                                    "line-length" => 76,
                                    "scheme" => "B"
                                );
                                $attachmentFilename = substr(iconv_mime_encode("", $filenameOriginal, $preferences),2);
                                //according to RFC 2046 message/rfc822 mimme types must be 7bit, 8bit, or binary encoded
                                $attachmentEncoding = ($attachment['mimeType'] == "message/rfc822") ? Zend_Mime::ENCODING_8BIT : Zend_Mime::ENCODING_BASE64 ;
                                //google likes message/rfc822 attachments to be inline
                                $attachmentDisposition = ($attachment['mimeType'] == "message/rfc822") ? Zend_Mime::DISPOSITION_INLINE : Zend_Mime::DISPOSITION_ATTACHMENT ;
                                $newMessage->createAttachment(
                                    $fileContents,
                                    $attachment['mimeType'],
                                    $attachmentDisposition,
                                    $attachmentEncoding,
                                    $attachmentFilename
                                );
                            }
                        }
                }break;
            }
        }
    }

	public static function mimeEncodedDecode($stringOrArray, $charset = null)
	{

		//e.g. Subject: =?gb2312?B?nHnUhyCy4srUIHRlc3Rpbmc=?=
        if ( !isset($charset) )
            $charset = 'UTF-8';
        
		//search for characteristic =?AAAAA?B?CCCCCCCCC?= in string and return if not found
		//$string = "=?gb2312?B?nHnUhyCy4srUIHRlc3Rpbmc=?=";
		
		//sometimes header passed as array
		if( !is_array($stringOrArray) )
			$array = array($stringOrArray );
		else
			$array = $stringOrArray;
			
		foreach( $array as &$string )
		{
		
			if( !preg_match("/=\?([^\?]+)\?([^\?])\?([^\?]+)\?=/", $string, $m) )
				continue;
			if( count($m) != 4 )
				continue;
//Zend_Registry::get('log')->debug( "\n" . print_r($m, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$m \n");
			
			$encodedCharset = $m[1];
			$stringEncoding = $m[2];
			$encodedString = $m[3];
		
			if( strtoupper($stringEncoding) == 'B' )
				$encodedString = base64_decode($encodedString);
			elseif( strtoupper($stringEncoding) == 'Q' )
				$encodedString = quoted_printable_decode($encodedString);
			else //unknown encoding
				continue;
//Zend_Registry::get('log')->debug( __METHOD__ . ' #' . __LINE__ . ': $encodedString=' . $encodedString);
			//decoding rule variations go here
		
			//Microsoft Word 12 appears to allow multiple character sets encoded as gb2312 - safely use GB18030 to decode the gb2312 and extended chars
			if( strtoupper($encodedCharset) == 'GB2312' )
				$encodedCharset = 'GB18030';
		    elseif( strtoupper($encodedCharset) == 'UNICODE-1-1-UTF-7' )
				$encodedCharset = 'UTF-7';
			elseif( strtoupper($encodedCharset) == 'KS_C_5601-1987' )
				$encodedCharset = 'euckr';
			
			$decodedString = iconv( $encodedCharset ,  "utf-8", $encodedString );
//Zend_Registry::get('log')->debug( "\n" . print_r($decodedString, true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$decodedString \n");
			$string = $decodedString;
			
		}
		if( is_array($stringOrArray) )
			return $array;
		else
			return $array[0];
		
	}
	
	
	public static function &returnPreparedMessageArray($message, $argsSupplied = array() ) 
	{
		

		$argsDefault = array('parentEmbeddedAttachmentPrefix' => '', 'siteBaseUrl' => Zend_Registry::get('siteBaseUrl'));
		$argsRequired = array('tmpFolderBaseName', 'folder', 'uniqueId');
	    
		foreach( $argsRequired as $argRequired ) 
		{
		
			if( 
				!array_key_exists( $argRequired, $argsSupplied ) 
				|| 
				( $argRequired == 'tmpFolderBaseName' && strlen($argsSupplied['tmpFolderBaseName']) == 0) 
				||
				( $argRequired == 'folder' && strlen($argsSupplied['folder']) == 0) 
				||
				( $argRequired == 'uniqueId' && strlen($argsSupplied['uniqueId']) == 0) 
			)
				throw new Atmail_Mail_Exception('invalid call to returnPreparedMessageArray: required argument missing: ' . $argRequired);
			
		}
		
		$args = array_merge($argsDefault, $argsSupplied);
		
		//folder must be in UTF7 format, same as it comes from the IMAP server
		$folderUTF7 = $args['folder'];
		$folderUTF7UrlencodedDouble = urlencode( urlencode( $folderUTF7 ) );
		
		//check for unpackAllAttachments flag (e.g. when editing drafts we need all draft attachments on disk)
		if( array_key_exists('unpackAllAttachments', $args) && $args['unpackAllAttachments'] == true )
		{
			
			$unpackAllAttachments = true;
			
		}
		else
		{
			
			$unpackAllAttachments = false;
		}
		
		$tmpFolderBaseName = $args['tmpFolderBaseName'];
		
		$uniqueid = $args['uniqueId'];
		$embeddedAttachmentPrefix = 1;
		
		//compose array to return containing:
		$preparedMessageArray = array();
		$preparedMessageArray['headersOriginal'] = $message->getHeaders(); 
		//$log = Zend_Registry::get('log');
//Zend_Registry::get('log')->debug( "\n" . print_r($preparedMessageArray['headersOriginal'], true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$preparedMessageArray['headersOriginal'] \n");
		
		$additionalHeaderFields = array();
		if( !isset($preparedMessageArray['headers']['uniqueid']) || $preparedMessageArray['headers']['uniqueid'] == '')
			$additionalHeaderFields = array('uniqueid' => $uniqueid );
		
		$preparedMessageArray['headers'] = $message->getProcessedHeaders( $additionalHeaderFields ); 
//Zend_Registry::get('log')->debug( "\n" . print_r($preparedMessageArray['headers'], true) . "\n" . __METHOD__ . ' #' . __LINE__ . ": \$preparedMessageArray['headers'] \n");
		
		$preparedMessageArray['bodyPreparedHtml'] = '';
		//not yet reliable and also requires additional call - TODO: consider utilizing by adding BODYSTRUCTIRE to message content call
		//$preparedMessageArray['attachmentsList'] = $message->getAttachmentsList(); //list of all uncategorised attachments
		$preparedMessageArray['forcedInlines'] = array(); //can be type with content (messages) or url
		$preparedMessageArray['forcedAttachments'] = array();
		$preparedMessageArray['cids'] = array();
		
		$textContent = '';
		$htmlContent = '';
		$foundHtmlPart = false;
		$foundTextPart = false; 
		$cidHashList = array(); //list of cids in body to convert to temporary file name urls once all parts processed
		
		$forcedInlineMimeTypes = array('image/gif', 'image/png', 'image/jpeg', 'image/pjpeg', 'message/rfc822');
		$forceToAttachment = array('application/pdf', 'text/html', 'text/plain');
		$existingPartFilenames = array();
		$filenamePrependId = 1;
				
		if( !$message->isMultipart() ) 
		{
			$partHeaders = $message->getHeaders();
			
			if(isset($partHeaders['content-type']) && preg_match('/text\/html/', $partHeaders['content-type'])) 
			{
				$htmlContent = self::returnDecodedPart($message);
				if($htmlContent != '')
				{
					$foundHtmlPart = true;
				}
			}
			elseif( isset($partHeaders['content-type']) && preg_match('/text\/calendar/', $partHeaders['content-type']) )
			{
				
				$icscontent = self::returnDecodedPart($message);

			    $partFilename = 'calendar' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.ics';
			    $partFilenameOriginal = $partFilename;
			    $partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );

			    //finally make partFilename FS safe and unique to folder and uniqueId
			    if( file_exists( $tmpFolderBaseName . $partFilenameFS ))
			    {
			    	unlink($tmpFolderBaseName . $partFilenameFS);
			    }
			    while(strlen($icscontent) - 1 > 0 && ($icscontent[strlen($icscontent)-1] == "\n" || $icscontent[strlen($icscontent) -1 ] == "\r"))
			    	$icscontent[strlen($icscontent)-1] = NULL;
	    	
			    $icscontent .= "\n";
			    $bytesWritten = file_put_contents( $tmpFolderBaseName . $partFilenameFS, $icscontent );
			    if( $bytesWritten == 0 )
			    {
			    	//gracefully handle currupted (zero length) mime parts
			    	unlink($tmpFolderBaseName . $partFilenameFS);
			    }
				else
				{
				    $preparedMessageArray['forcedInlines'][] = array('iconClass' => 'ics', 'mimeType' => 'text/calendar', 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'content' => $icscontent, 'sizeRaw' => $message->getSize(), 'fileType' => 'ics' );
					$preparedMessageArray['attachmentsList'][] = array('iconClass' => 'ics', 'mimeType' => 'text/calendar', 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'content' => $icscontent, 'sizeRaw' => $message->getSize(), 'fileType' => 'ics' );
					
					
				}
			}
			elseif( isset($partHeaders['content-type']) ) 
			{
				
				

				$partFilename = null;
				// we found an email with illegal multiple content types use last
				if( is_array($partHeaders['content-type']) )
					$partHeaders['content-type'] = $partHeaders['content-type'][count($partHeaders['content-type'])-1];
				
				preg_match('|([^/]+)/([^;]+)|', $partHeaders['content-type'], $contentTypeParts);
				preg_match( '@([^;]+)([^(charset=)]+charset=[\s"\']?([\w-\.]+)[\s"\']?)?([^(name=)]+name=[\s]?["\']?([^"\';]+)["\']?)?@i' , $partHeaders['content-type'], $contentTypeMatches);

				$contentType = $contentTypeParts[0];
				if($contentType == 'image/pjpeg')
				{
					$contentType = 'image/jpeg';
				}
				
				//if we find a 6th match then is matched a filename in the content-type
				if( count($contentTypeMatches) == 6 && !empty($contentTypeMatches[5]))
				{
					
					$partFilename = trim($contentTypeMatches[5], '"\' ');
				
				}
				//now if we find a filename then force to attachment so that we can generate preview, download link etc. else return content as basic message body.
				else if( isset($partHeaders['content-disposition']) && preg_match('/name=\s*([^;]+)/i', $partHeaders['content-disposition'], $contentDispositionMatches) )
					$partFilename = trim($contentDispositionMatches[1], '"\' ');
				else
				{
				
					if( isset($contentTypeMatches[3]) )
						$charset = $contentTypeMatches[3];
					else
						$charset = 'UTF-8';
					//convert to UTF-8, all internals are utf-8
					$textContent = self::returnDecodedPart($message);
					$foundTextPart = true;
				}
				
				if( $partFilename !== null )
				{
					
					//basic checking to not decode an already decoded filename
					if( strpos( $partFilename, '=?') !== false && strpos( $partFilename, '?=') !== false)
						$partFilename = iconv_mime_decode($partFilename, 0, "UTF-8");   

					//add on folderUTF7 and UID
					$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );

					//finally make partFilename FS safe and unique to folder and uniqueId
					if( file_exists($tmpFolderBaseName . $partFilenameFS ))
						unlink($tmpFolderBaseName . $partFilenameFS);
				
					$content = self::returnDecodedPart($message);
					
                	
					$bytesWritten = file_put_contents( $tmpFolderBaseName . $partFilenameFS, $content );
					if( $bytesWritten == 0 )
						unlink($tmpFolderBaseName . $partFilenameFS);

					
					if( isset($contentTypeParts[2]) ) {
						$fileType = $contentTypeParts[2];
					}
					else {
						$fileType = 'default';
					}
					$icon = Atmail_Message_Handler_Enum::$iconClass[$fileType];
					$content = '';

					if( in_array($contentType, $forcedInlineMimeTypes) )
						$preparedMessageArray['forcedInlines'][] = array('iconClass' => $icon, 'mimeType' => $contentType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilename, 'content' => $content, 'sizeRaw' => $message->getSize(), 'fileType' => $fileType );
					elseif( in_array($contentType, $forceToAttachment) )
						$preparedMessageArray['forcedAttachments'][] = array('iconClass' => $icon, 'mimeType' => $contentType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilename, 'content' => $content, 'sizeRaw' => $message->getSize(), 'fileType' => $fileType );
					
					$preparedMessageArray['attachmentsList'][] = array('iconClass' => $icon, 'mimeType' => $contentType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilename, 'content' => $content, 'sizeRaw' => $message->getSize(), 'fileType' => $fileType );

						

					   
				}
				
			}
	
		}

		foreach (new RecursiveIteratorIterator($message, RecursiveIteratorIterator::SELF_FIRST) as $part) 
		{
		
			$partHeaders = $part->getHeaders();

			//try match filename here as may be needed at various parts
			$partFilename = 'unknownfilename';
			$contentDispositionMatches = array();
			$matches = array();
			$mimeType = 'unknown/unknown';
			if( isset($partHeaders['content-type']) ) 
			{
				
				// we found an email with illegal multiple content types use last
				if( is_array($partHeaders['content-type']) )
					$partHeaders['content-type'] = $partHeaders['content-type'][count($partHeaders['content-type'])-1];
				preg_match( '@([^;]+)([^(charset=)]+charset=[\s"\']?([\w-\.]+)[\s"\']?)?([^(name=)]+name=[\s]?["\']?([^"\';]+)["\']?)?@i' , $partHeaders['content-type'], $matches);
				
			}
            
			//we found an email with multiple content dispositions (use last type and hope for the best)
			if( isset($partHeaders['content-disposition']) && is_array($partHeaders['content-disposition']) ) 
			{
				
				//TODO: find better way to handle multiple content-disposition part header lines.
				$longestStringFoundId = 0;
				foreach($partHeaders['content-disposition'] as $k => $v ) 
				{
					if( strlen($v) > strlen($partHeaders['content-disposition'][$longestStringFoundId]) )
						$longestStringFoundId = $k;
				}                                  
				$partHeaders['content-disposition'] = $partHeaders['content-disposition'][$longestStringFoundId];
				
			}
			
			if( isset($matches[5]) )
				$partFilename = $matches[5];
			elseif( isset($partHeaders['content-disposition']) && preg_match('/name=\s*([^;]+)/i', $partHeaders['content-disposition'], $m) )
				$partFilename = trim($m[1], '"'); //strip wrapping quotation marks
			elseif(	
					isset($partHeaders['content-disposition'])
					&&
					preg_match( '@([\w/-]+)([^(filename=)]+filename=[\s"\']?([^"\';]+)["\']?)?([^(name=)]+name=[\s]?["\']?([^"\';]+)["\']?)?@i' , $partHeaders['content-disposition'], $contentDispositionMatches)
					&&
					isset($contentDispositionMatches[3]) 
			) 
				$partFilename = $contentDispositionMatches[3];
			
			elseif( 
					isset($partHeaders['content-disposition']) 
					&& 
					preg_match_all('/filename(\*\d+)="(.+?)"/i', $partHeaders['content-disposition'], $m, PREG_PATTERN_ORDER) 
			)
				$partFilename = join('', $m[2]); //some clients split up file names in the header
			
			elseif( 
				   	$partFilename == 'unknownfilename' 
					&&
			 		isset($partHeaders['content-type']) 
					&&
					preg_match_all('/name(\*\d+)="(.+?)"/i', $partHeaders['content-type'], $m, PREG_PATTERN_ORDER) 
			)
				$partFilename = join('', $m[2]); //some clients split up file names in the header                      
			
			elseif( 
					$partFilename == 'unknownfilename' &&
				 	isset($partHeaders['content-type']) &&
				 	preg_match_all('/name=\s*([^;]+)/i', $partHeaders['content-type'], $m, PREG_PATTERN_ORDER) 
			) 
				
				$partFilename = str_replace('"', '', $m[1][0]);
			elseif( isset($partHeaders['content-description']) )
				$partFilename = str_replace('"', '', $partHeaders['content-description']);
			elseif( isset($partHeaders['content-location']) )
				$partFilename = str_replace('"', '', $partHeaders['content-location']);
			
			elseif( 
					$partFilename == 'unknownfilename' 
					&&
					isset($partHeaders['content-disposition']) 
					&&
			 		strpos($partHeaders['content-disposition'], '*') !== false 
			)
			{
				
				/*
				See: http://www.faqs.org/rfcs/rfc2231.html
				RFC 2231 - MIME Parameter Value and Encoded Word Extensions: Character Sets, Languages, and Continuations
				This desribes the iMail style of encoding i18n attachment filenames
				Not implimented in A5 
				//test possibly existing support in:                          
				// array imap_mime_header_decode ( string $text )
				// string iconv_mime_decode ( string $encoded_header [, int $mode [, string $charset]] )
				*/
				$result = self::decodeRFC2231Header($partHeaders['content-disposition']);
				
				if( strlen($result['value']) > 0 )
					$partFilename = $result['value'];
			
			}
			elseif( $matches[1] == "text/calendar" ) //else if no filename but still a text/calendar, then create a nice cal filename.ics
			{
				
				if( $partFilename == 'unknownfilename')
					$partFilename = 'calendar' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.ics';
				$partFilenameOriginal = $partFilename;
				$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
				
			}
			
			
			
			if( isset($partHeaders['content-type']) ) 
			{
				
				$mimeType = strtolower($matches[1]);
				$mimeTypeParts = explode('/', $mimeType, 2);
				if($mimeType == 'image/pjpeg')
				{
					$mimeType = 'image/jpeg';
				}
				
				//if is multipart part continue to next part
				if( $mimeTypeParts[0] == 'multipart' )
					continue;
		        
				if( $mimeType == 'text/plain' || $mimeType == 'message/delivery-status') 
				{
					
					if( 
						( !$foundTextPart || $mimeType == 'message/delivery-status' )
						&& 
						( !isset($partHeaders['content-disposition']) || (isset($partHeaders['content-disposition']) && strpos($partHeaders['content-disposition'], 'name') === false) )
						&&
						( !isset($partHeaders['content-type']) || (isset($partHeaders['content-type']) && strpos($partHeaders['content-type'], 'name') === false) )
						 
					) 
					{
						$textContent .= self::returnDecodedPart($part);
						$foundTextPart = true;
						//don't continue unpacking the first text part as the main email text part (non downloadable without downloading the entire .eml)
						continue;          
					
					}
					else
					{
					
						$content = self::returnDecodedPart($part);
						//ignore if larger than x
						//TODO: force to attachment if larger than x
						if( strlen($content) > (1024*5) )
							$content = '';
						if( $partFilename == 'unknownfilename')
							$partFilename = 'embeddedtext' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.txt';
						$partFilenameOriginal = $partFilename;
						$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
						
						//check if unpackAllAttachments
						if( $unpackAllAttachments )
						{

							//finally make partFilename FS safe and unique to folder and uniqueId
							if( file_exists($tmpFolderBaseName . $partFilenameFS ))
							{
								
								unlink($tmpFolderBaseName . $partFilenameFS);
								
							}

							$bytesWritten = file_put_contents($tmpFolderBaseName . $partFilenameFS, self::returnDecodedPart( $part ) );

							if( $bytesWritten == 0 )
							{

								//gracefully handle currupted (zero length) mime parts
								unlink($tmpFolderBaseName . $partFilenameFS);
								continue;

							}

						}
						
						preg_match('/\.(\w+)$/', $partFilenameOriginal, $m);
						
						if(isset(Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])]))
							$iconState = Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])];
						else
							$iconState = Atmail_Message_Handler_Enum::$iconClass['default'];
						
						if(isset(Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])]))
							$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])];
						else
							$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames['default'];
				
						$preparedMessageArray['forcedInlines'][]   = array('iconClass' => Atmail_Message_Handler_Enum::$iconClass['plain'], 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'content' => nl2br(htmlspecialchars($content)), 'sizeRaw' => $part->getSize(), 'fileTypeName' => $fileTypeName );
						$preparedMessageArray['attachmentsList'][] = array('iconClass' => Atmail_Message_Handler_Enum::$iconClass['plain'], 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'content' => nl2br(htmlspecialchars($content)), 'sizeRaw' => $part->getSize(), 'fileTypeName' => $fileTypeName );
												
						continue;
						
					}
				} 
				else if( $mimeType == 'text/html' ) 
				{					
					
					if( 
						( !isset($partHeaders['content-disposition']) || (isset($partHeaders['content-disposition']) && strpos($partHeaders['content-disposition'], 'name') === false) )
						&&
						( !isset($partHeaders['content-type']) || (isset($partHeaders['content-type']) && strpos($partHeaders['content-type'], 'name') === false) )
					) 
					{ 
						$htmlContentNew = self::returnDecodedPart($part);
						if($htmlContentNew != '')
						{
							$htmlContent .= $htmlContentNew;
							$foundHtmlPart = true;
						}
						//don't unpacking the first html part as the main email text part (normal to be non downloadable without downloading the entire .eml)
						continue;
						
					}
					else
					{
						
						$content = self::returnDecodedPart($part);
													
						if( $partFilename == 'unknownfilename')
							$partFilename = 'embeddedhtml' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.html';
						$partFilenameOriginal = $partFilename;
						$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
						
						// check if unpackAllAttachments
						if( $unpackAllAttachments )
						{

							//finally make partFilename FS safe and unique to folder and uniqueId
							if( file_exists($tmpFolderBaseName . $partFilenameFS ))
							{
								
								unlink($tmpFolderBaseName . $partFilenameFS);
								
							}

							$bytesWritten = file_put_contents($tmpFolderBaseName . $partFilenameFS, self::returnDecodedPart( $part ) );

							if( $bytesWritten == 0 )
							{

								//gracefully handle currupted (zero length) mime parts
								unlink($tmpFolderBaseName . $partFilenameFS);
								continue;

							}

						}
						
						preg_match('/\.(\w+)$/', $partFilenameOriginal, $m);
						
						if(isset(Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])]))
							$iconState = Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])];
						else
							$iconState = Atmail_Message_Handler_Enum::$iconClass['default'];
						
						if(isset(Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])]))
							$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])];
						else
							$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames['default'];
						
						$preparedMessageArray['forcedInlines'][] = array('iconClass' => Atmail_Message_Handler_Enum::$iconClass['html'], 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'content' => $content, 'sizeRaw' => $part->getSize(), 'fileTypeName' =>  $fileTypeName);
						$preparedMessageArray['attachmentsList'][] = array('iconClass' => Atmail_Message_Handler_Enum::$iconClass['html'], 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'content' => $content, 'sizeRaw' => $part->getSize(), 'fileTypeName' =>  $fileTypeName);
						
						continue;
						
					}
					
				}
				else if( $mimeType == 'message/rfc822' ) 
				{					
    				$inlineMessage = new Atmail_Mail_Message( array('raw' => self::returnDecodedPart($part)) );
					//$inlineMessage->processHeaders(  array('uniqueid' => $preparedMessageArray['headers']['uniqueid'])  );
					$contentArray = self::returnPreparedMessageArray($inlineMessage, array('parentEmbeddedAttachmentPrefix' => $args['parentEmbeddedAttachmentPrefix'] . $embeddedAttachmentPrefix, 'tmpFolderBaseName' => $args['tmpFolderBaseName'], 'folder' => $folderUTF7, 'uniqueId' => $args['uniqueId']));
					
					if( $partFilename == 'unknownfilename')
						$partFilename = 'embeddedEmail' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.eml';
					$partFilenameOriginal = $partFilename;
					$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
					
					preg_match('/\.(\w+)$/', $partFilenameOriginal, $m);
                    if(isset(Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])]))
						$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])];
					else
						$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames['default'];
					
					// check if unpackAllAttachments
					if( $unpackAllAttachments )
					{

						//finally make partFilename FS safe and unique to folder and uniqueId
						if( file_exists($tmpFolderBaseName . $partFilenameFS ))
						{
							
							unlink($tmpFolderBaseName . $partFilenameFS);
							
						}
                        $bytesWritten = file_put_contents($tmpFolderBaseName . $partFilenameFS, self::returnDecodedPart( $part ) );
                        
						if( $bytesWritten == 0 )
						{

							//gracefully handle currupted (zero length) mime parts
							unlink($tmpFolderBaseName . $partFilenameFS);
							continue;

						}

					}
					
					$preparedMessageArray['forcedInlines'][]   = array('iconClass' => Atmail_Message_Handler_Enum::$iconClass['message'], 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $part->getSize(), 'fileTypeName' => $fileTypeName, 'content' => $contentArray );
					$preparedMessageArray['attachmentsList'][] = array('iconClass' => Atmail_Message_Handler_Enum::$iconClass['message'], 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $part->getSize(), 'fileTypeName' => $fileTypeName);
					
					continue;
					
				}
		   
			}
			
		   	//ZF encoded attachment filename broken so decode here (some CLI issues here)
		   	if( strpos($partFilename, '=?') !== false && strpos( $partFilename, '?=') !== false )
			{
				
		   		$partFilename = iconv_mime_decode($partFilename, 0, "UTF-8"); 
		
			}
			
			if( isset($partHeaders['content-id']) ) 
			{
                //most inlines CIDs are images
				$fileExtension = '';
				if( $mimeType == 'image/jpeg' )
					$fileExtension = '.jpg';
				elseif( $mimeType == 'image/gif' )
					$fileExtension = '.gif';
				elseif( $mimeType == 'image/png' )
					$fileExtension = '.png';
				
				if( strpos($partFilename, 'unknownfilename') !== false && isset($partHeaders['content-location']) )
					$partFilename = $partHeaders['content-location'];
				elseif( strlen($fileExtension) > 0 && substr( $partFilename, -4) != $fileExtension )
				{
					//catch .jpeg && ($fileExtension !=)
					if( substr( $partFilename, -5) != '.jpeg' )
						$partFilename .= $fileExtension;	
				}
				
				
				
				if( in_array( $partFilename, $existingPartFilenames ) ) 
				{
					
					//attachment name already used (lazy email clients) so increment the filename before writing it to disk
					//insert an ordinal number number into unknown filenames so attachment fetcher can fetch correct attachment
					$filenamePrependId++;
					$partFilename = (String)$filenamePrependId . $partFilename;
				
				} 
				$existingPartFilenames[] = $partFilename;
				
				$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
				
				if( strpos($partFilename, 'unknownfilename') !== false )
					$partFilename = $folderUTF7 . $args['uniqueId'] . md5($partFilename);
				
				//finally make partFilename FS safe and unique to folder and uniqueId
				$partFilenameOriginal = $partFilename;
				
				if( file_exists($tmpFolderBaseName . $partFilenameFS ))
				{
					
					unlink($tmpFolderBaseName . $partFilenameFS);
					
				}
                
				$bytesWritten = file_put_contents($tmpFolderBaseName . $partFilenameFS, self::returnDecodedPart( $part ) );

				if( $bytesWritten == 0 )
				{

					//gracefully handle currupted (zero length) mime parts
					unlink($tmpFolderBaseName . $partFilenameFS);
					continue;

				}

				preg_match('/\.(\w+)$/', $partFilenameOriginal, $m);
				
				if(isset(Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])]))
				$iconState = Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])];
				else
				$iconState = Atmail_Message_Handler_Enum::$iconClass['default'];
				
				if(isset(Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])]))
				$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])];
				else
				$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames['default'];
				
				$cid = $partHeaders['content-id'];
				if(substr($cid,0,1) == "<")
					$cid = substr($cid, 1);
				if(substr($cid, -1) == ">")
					$cid = substr($cid, 0, -1);  
				//TODO: prevent duplicates
				$preparedMessageArray['cids'][]            = array('iconClass' => $iconState, 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $bytesWritten, 'fileTypeName' => $fileTypeName, 'cid' => $cid);
				$preparedMessageArray['attachmentsList'][] = array('iconClass' => $iconState, 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $bytesWritten, 'fileTypeName' => $fileTypeName);
				
				//CONSIDER: consider forcing to forcedInlines for cid inlines as well
				continue;

			}   			
				
			//finally make partFilename URL and FS safe and unique to folder and uniqueId
			$partFilenameOriginal = $partFilename;
			$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
			
			preg_match('/\.(\w+)$/', $partFilenameOriginal, $m);

			if( isset($m[1]) && isset(Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])]))
			$iconState = Atmail_Message_Handler_Enum::$iconClass[strtolower($m[1])];
			else
			$iconState = Atmail_Message_Handler_Enum::$iconClass['default'];
			
			if(isset($m[1]) && isset(Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])]))
			$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames[strtolower($m[1])];
			else
			$fileTypeName = Atmail_Message_Handler_Enum::$fileTypeNames['default'];
			
			//now try to match for other part types
			if( in_array($mimeType, $forcedInlineMimeTypes) ) 
			{ 
                
				if( file_exists($tmpFolderBaseName . $partFilenameFS ))
				{
					
					unlink($tmpFolderBaseName . $partFilenameFS);
					
				}

				$sizeRaw = file_put_contents($tmpFolderBaseName . $partFilenameFS, self::returnDecodedPart( $part ) );
				
				if( $sizeRaw == 0 ) 
				{
					
					//gracefully handle currupted (zero length) mime parts
					unlink($tmpFolderBaseName . $partFilenameFS);
					continue;
				
				}
				

				$preparedMessageArray['forcedInlines'][]   = array('iconClass' => $iconState, 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $sizeRaw, 'fileTypeName' => $fileTypeName );
				$preparedMessageArray['attachmentsList'][] = array('iconClass' => $iconState, 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $sizeRaw, 'fileTypeName' => $fileTypeName);
				
				
			} 
			else 
			{

				//mimetypes to cache do disk
				$cacheToDisk = array('application/pdf');
				if( in_array($mimeType, $cacheToDisk) || $unpackAllAttachments ) 
				{
					if( file_exists($tmpFolderBaseName . $partFilenameFS ))
						unlink($tmpFolderBaseName . $partFilenameFS);

					$sizeRaw = file_put_contents ($tmpFolderBaseName . $partFilenameFS, self::returnDecodedPart( $part ) );

					if( $sizeRaw == 0 ) 
					{

						//gracefully handle currupted (zero length) mime parts
						unlink($tmpFolderBaseName . $partFilenameFS);
						continue;

					}
				}
				
				$preparedMessageArray['forcedAttachments'][] = array('iconClass' => $iconState, 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $part->getSize(), 'fileTypeName' => $fileTypeName);
				$preparedMessageArray['attachmentsList'][]   = array('iconClass' => $iconState, 'mimeType' => $mimeType, 'filenameFS' => $partFilenameFS, 'filenameOriginal' => $partFilenameOriginal, 'sizeRaw' => $part->getSize(), 'fileTypeName' => $fileTypeName);

				
			}
						
		} //EO foreach recursion

		//if html part found use html part else prep and include text part 
		if( $foundHtmlPart ) 
		{
			foreach( $preparedMessageArray['cids'] as $hash ) 
			{
				
				//force as forcedInline if cid not found in html
				//all URLvars that could contain chars that confuse url parsing or ZF url handline (/ ? & etc.) must be double urlencoded because ZF auto decodes before looking for SEF vars and gets confused if only single urlencoded
				$mimeTypeUrlencodedDouble = urlencode( urlencode( $hash['mimeType']) );

				//consider moving this the html cid replacement to the rendering portion
				$imgSrc = $args['siteBaseUrl'] . 'index.php/mail/viewmessage/getattachment/folder/' . $folderUTF7UrlencodedDouble . '/uniqueId/' . $uniqueid .'/mimeType/' . $mimeTypeUrlencodedDouble . '/filenameOriginal/' . md5($hash['filenameOriginal']);
				if( strpos($htmlContent, $hash['cid']) !== false )
					$htmlContent = str_ireplace('cid:' . $hash['cid'], $imgSrc, $htmlContent);
				else
					$preparedMessageArray['forcedInlines'][] = $hash;
			
			}
			$preparedMessageArray['bodyPreparedHtml'] = $htmlContent;
		} 
		elseif( $foundTextPart ) 
		{	
			$textContent = nl2br(htmlspecialchars($textContent));
			$preparedMessageArray['bodyPreparedHtml'] = $textContent;

			foreach( $preparedMessageArray['cids'] as $hash ) 
			{
				//force as forcedInline if cids exist (illegal, but gracefully handle - some MMS clients do may this)
				$preparedMessageArray['forcedInlines'][] = $hash;
			}
		
		} 
		else 
		{
			foreach( $preparedMessageArray['cids'] as $hash ) 
			{
				//force as forcedInline if unmatched cids exist (illegal, but gracefully handle - some MMS clients may do this)
				$preparedMessageArray['forcedInlines'][] = $hash;
			}
		
		}
		return $preparedMessageArray;

	}
	
	public static function removeScriptsFromDOM($root)
	{
		
		// get all the script tags
		$script_tags = $root->getElementsByTagName('script');

		while( $myItem = $script_tags->item(0) ){
			$myItem->parentNode->removeChild($myItem);
		}

		return $root;
	}
	
	public static function loadAndPrepareDOM($contentUTF8)
	{
		
		if ( !empty($contentUTF8))
		{
			//strip attributes out of html tags
			$matcheC = preg_match_all( '/\<html.{0,1000}\>/i', $contentUTF8, $matches, PREG_OFFSET_CAPTURE, 3);
			if( $matcheC > 1 )
			{
				$skippedFirst = false;
				foreach( $matches[0] as $match )
				{
					if( !$skippedFirst )
					{
						
						$skippedFirst = true;
						continue;
						
					}
					$contentUTF8 = substr($contentUTF8,0,$match[1]) . substr($contentUTF8,$match[1]+strlen($match[0]));
					
				}
				
			}
			
			//strip http-equiv tags - we always decode and send to the ui as utf-8
			$contentUTF8PregReplaced = preg_replace( '/\<meta.*?http-equiv.*?\>/i', '', $contentUTF8, 1 );
			if( strlen($contentUTF8PregReplaced) !== 0 )
				$contentUTF8 = $contentUTF8PregReplaced;
			
			
			$headpos = mb_strpos($contentUTF8,'<head>');
			if (FALSE === $headpos)
			{
				$headpos= mb_strpos($contentUTF8,'<HEAD>');
			}
			if (FALSE!== $headpos)
			{
				$headpos+=6;
				$contentUTF8 = mb_substr($contentUTF8,0,$headpos) . "\n" . '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' .mb_substr($contentUTF8,$headpos);
			}
			$contentUTF8=mb_convert_encoding($contentUTF8, 'HTML-ENTITIES', "UTF-8");
		}
		$dom = new DomDocument;
		$res = @$dom->loadHTML($contentUTF8);
		if (!$res)
		{
			return FALSE;
		}
		return $dom;
	}
	
	public static function returnDecodedPart($part) 
	{
		//takes a part and decodes it into utf-8 or 8bit (binary)
		//RULES: if Content-Type contains name= then return as 8bit else utf-8
		$headers = $part->getHeaders();
		
		$transferEncoding = isset($headers['content-transfer-encoding'])?strtolower($headers['content-transfer-encoding']):'8bit';
		$content = $part->getContent();
		
		if( $transferEncoding == 'quoted-printable' )
			$content = quoted_printable_decode( $content );
		elseif( $transferEncoding == 'base64' )
			$content = base64_decode( $content );

		// transfer encoding of '7bit', '8bit', 'binary' mean that no binary-to-text encoding on top of the original encoding was used.
		// so the content remains unchanged.
			
		// only convert character sets to UTF-8 if Content-Type contains charset
		// charset encoded data is human readable so can be a bit more lenient on attempting to decode possible corrupted/badly encoded data
			
		if( isset($headers['content-type']) ) 
		{
			if( is_array($headers['content-type']) )
				$headers['content-type'] = $headers['content-type'][count($headers['content-type'])-1];

			$contentTypeMatches = array();
			preg_match( '@([^;]+)([^(charset=)]+charset=[\s"\']?([\w-\.]+)[\s"\']?)*@i' , $headers['content-type'], $contentTypeMatches);
            
			if(count($contentTypeMatches) > 2 && strlen($contentTypeMatches[3]) > 0 ) 
			{
			
				$charset = strtoupper($contentTypeMatches[3]);
				
				//convert from charset to UTF-8
				if( strpos($headers['content-type'], 'octet-stream') !== false )
				{
					// skip encoding for binary data
				}
				elseif( $charset == 'ISO-8859-1' || $charset == 'ISO8859-1')
				{
					$content = utf8_encode( $content );
				}
				elseif( $charset == 'UTF-4' )
				{
				    $content = iconv( 'UTF-8',  "utf-8//IGNORE", $content);
				}
				elseif( $charset == 'UNICODE-1-1-UTF-7' )
				{
				    $content = iconv( 'UTF-7',  "utf-8//IGNORE", $content);
				}
				elseif( $charset == 'GB2312' )
				{
					$content = iconv( 'GB18030',  "utf-8//IGNORE", $content);
				}
				elseif( $charset == 'KS_C_5601-1987' )
				{
					$content = iconv( 'euckr',  "utf-8//IGNORE", $content);
				}
				elseif( $charset != 'UTF-8' )
				{
					//gracefully handle unknown/illegal/bad charactersets
					if( in_array($charset, array('UNKNOWN-8bit')) )
						$charset = 'ASCII';
					$contentFinal = iconv( $charset,  "utf-8//IGNORE", $content);
					if( strlen($contentFinal) == 0 )
					{
						$contentFinal = iconv( 'UTF-8',  "utf-8//IGNORE", $content);
						if( strlen($contentFinal) == 0 )
						{
							$contentFinal = iconv( 'ASCII-US',  "utf-8//IGNORE", $content);
						}	
					}
					if( strlen($contentFinal) > 0)
						$content = $contentFinal;
					
				}
			}
			elseif( substr($headers['content-type'],0,9) == 'text/html' || substr($headers['content-type'],0,10) == 'text/plain' )
			{
				$charset = mb_detect_encoding($content, '7bit, ASCII, WINDOWS-1252, UTF7, UTF8, ISO-8859-1', true);
				$content = mb_convert_encoding($content, 'UTF-8', $charset);
			}

			// turn this on to leverage DOMDocument html verification.
			// Experimental. Currently destroys some encoding types.
			$contentVerification = true;
				
			if($contentVerification && substr($headers['content-type'],0,9) == 'text/html' )
			{
				$res = true;

				// process the html provided - could be malformed.
				// we try a few different ways before giving up and stripping the html tags
				if (!empty($content))
				{
					$dom = self::loadAndPrepareDOM($content);
					if ($dom === FALSE)
	                {
						// retry with purified version
	                	$myPurifier = new Atmail_Filter_HTMLPurifier();
	                	$content = $myPurifier->filter($content);                		
	                	$res = ($content == false || $content == '') ? false : true;
	
	                	if($res)
	                	{
							$dom = self::loadAndPrepareDOM($content);
		                	if ($dom)
		                	{
		                		// success!
		                		$dom = self::removeScriptsFromDOM($dom);
		                		//$dom = self::removeEventsFromDOM($dom);
		                		$content = $dom->saveHTML();
		                	}
	                	}
	                }
	                else
	                {
	                	// success!
	                	$dom = self::removeScriptsFromDOM($dom);
//		                $dom = self::removeEventsFromDOM($dom);
	                	$content = $dom->saveHTML();
	                }

	                if(!$res)
	                {
	                	// complete failure.
	                	// lets strip tags and attempt to render text version
	                	require_once('class.html2text.inc');
	                	$content = new html2text($content);
	                	$content = $content->get_text();
	                	$content = nl2br($content);
	                }            
				}				
			}
		}
		return $content;		
	}
	
	/**
	 * $partFilename must be UTF-8
	 */
	
	public static function recursivelySearchMessageForAttachment($messageObject, $argsSupplied = array(), &$file) 
	{
		
		//recursively goes through a message looking for a requested attachment
		$argsDefault = array('parentEmbeddedAttachmentPrefix' => '');
		$argsRequired = array('folder', 'uniqueId', 'filename');
		foreach( $argsRequired as $argRequired ) 
			if( !array_key_exists( $argRequired, $argsSupplied) )
				throw new Atmail_Mail_Exception('invalid call to recursivelySearchMessageForAttachment: required argument missing: ' . $argRequired);
		
		$args = array_merge($argsDefault, $argsSupplied);
		
		$embeddedAttachmentPrefix = 1;
		$existingPartFilenames = array();
		$filenamePrependId = 1;
		
		$found = false;

		if( !$messageObject->isMultipart() ) 
		{
		    
			$messageHeaders = $messageObject->getHeaders();
			
			//if we get here, then we have not found the attachment after recursing all parts of this node so perhapes this is a force mimetype attachment
			// lets check
            if(isset($messageHeaders['content-type']) && preg_match('/text\/calendar/', $messageHeaders['content-type'])) 
			{
				// its the main content type of the message which we have forced as an attachment
				$folderUTF7 = $args['folder'];
				$partFilename = 'calendar' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix) . '.ics';
				$partFilenameOriginal = $partFilename;
				$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
				if($folderUTF7 . $args['uniqueId'] . $args['filename'] == $partFilenameFS)
                {
                    $file = array();
                    $file['filename'] = $partFilename;
                    $file['mimeType'] = $messageHeaders['content-type'];
                    $file['content'] = self::returnDecodedPart($messageObject);
					return true;
                }
			}
			
			
			if( isset($messageHeaders['content-type']) ) 
			{

				// we found an email with illegal multiple content types use last
				if( is_array($messageHeaders['content-type']) )
					$messageHeaders['content-type'] = $messageHeaders['content-type'][count($messageHeaders['content-type'])-1];
				
				preg_match('|([^/]+)/([^;]+)|', $messageHeaders['content-type'], $contentTypeParts);
				preg_match( '@([^;]+)([^(charset=)]+charset=[\s"\']?([\w-\.]+)[\s"\']?)?([^(name=)]+name=[\s]?["\']?([^"\';]+)["\']?)?@i' , $messageHeaders['content-type'], $contentTypeMatches);
				$contentType = $contentTypeParts[0];
								
				if( isset($messageHeaders['content-disposition']) && preg_match('/name=\s*([^;]+)/i', $messageHeaders['content-disposition'], $contentDispositionMatches) )
					$partFilename = trim($contentDispositionMatches[1], '"\' ');
				else
				{
				
					$partFilename = $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . 'unknown';
					if( isset($contentTypeParts[2]) )
						$partFilename .= '.' . $contentTypeParts[2];
				
				}
				//basic checking to not decode an already decoded filename
				if( strpos( $partFilename, '=?') !== false && strpos( $partFilename, '?=') !== false)
					$partFilename = iconv_mime_decode($partFilename, 0, "UTF-8");   
                
				if( $partFilename == $args['filename'] )
				{

					$file = array();
					$file['filename'] = $partFilename;
					$file['mimeType'] = $contentType;
					$file['content'] = self::returnDecodedPart($messageObject);
					return true;
					
				}
				
			}
	
		}

		foreach (new RecursiveIteratorIterator($messageObject, RecursiveIteratorIterator::SELF_FIRST) as $part) 
		{ 
		    
			$partHeaders = $part->getHeaders();
			
			$mimeType = 'unknown/unknown';
			if( isset($partHeaders['content-type']) ) 
			{

				$matches = array();
				preg_match( '@([^;]+)([^(charset=)]+charset=[\s"\']?([\w-\.]+)[\s"\']?)?([^(name=)]+name=[\s]?["\']?([^"\';]+)["\']?)?@i' , $partHeaders['content-type'], $matches);
				
				$mimeType = $matches[1];
						
				$mimeTypeParts = explode('/', $mimeType, 2);
				//if is multipart part continue to next part
				if( $mimeTypeParts[0] == 'multipart' )
					continue;
				
			}
			
			//now try to match the filename
			$partFilename = 'unknownfilename';
			$contentDispositionMatches = array();
			if( isset($matches[5]) )
				$partFilename = $matches[5];
			elseif( isset($partHeaders['content-disposition']) && preg_match('/filename=\s*([^;]+)/i', $partHeaders['content-disposition'], $m) )
				$partFilename = trim($m[1], '"'); //strip wrapping quotation marks
			elseif(	
					isset($partHeaders['content-disposition'])
					&& 
					preg_match( '@([\w/-]+)([^(filename=)]+filename=[\s"\']?([^"\';]+)["\']?)?([^(name=)]+name=[\s]?["\']?([^"\';]+)["\']?)?@i' , $partHeaders['content-disposition'], $contentDispositionMatches)
					&& 
					isset($contentDispositionMatches[3]) 
			)
		    	$partFilename = $contentDispositionMatches[3];
		    elseif( 
					isset($partHeaders['content-disposition']) 
					&& 
					preg_match_all('/filename(\*\d+)="(.+?)"/i', $partHeaders['content-disposition'], $m, PREG_PATTERN_ORDER) 
			)
				$partFilename = join('', $m[2]); //some clients split up file names in the header
			elseif( 
					empty($partFilename) 
					&&
			 		isset($partHeaders['content-type']) 
					&&
					preg_match_all('/name(\*\d+)="(.+?)"/i', $partHeaders['content-type'], $m, PREG_PATTERN_ORDER) 
			)
				$partFilename = join('', $m[2]); //some clients split up file names in the header
			elseif( 
				$partFilename == 'unknownfilename' 
				&&
			 	isset($partHeaders['content-type']) 
				&&
				preg_match_all('/name=\s*([^;]+)/i', $partHeaders['content-type'], $m, PREG_PATTERN_ORDER) 
			) 
			{
				$partFilename = str_replace('"', '', $m[1][0]);
				$foundIt = true;
			} 
			//elseif( isset($partHeaders['content-id']) && preg_match( '@[^\@<>]+@i' , $partHeaders['content-id'], $cidmatches) )
			//	$partFilename = $cidmatches[0];
			elseif( isset($partHeaders['content-description']) )
				$partFilename = str_replace('"', '', $partHeaders['content-description']);
			elseif( isset($partHeaders['content-location']) )
				$partFilename = str_replace('"', '', $partHeaders['content-location']);
			
			elseif(
				$partFilename == 'unknownfilename' 
				&&
				isset($partHeaders['content-disposition']) 
				&&
				strpos($partHeaders['content-disposition'], '*') !== false 
			)
			{
				
				/*
				See: http://www.faqs.org/rfcs/rfc2231.html
				RFC 2231 - MIME Parameter Value and Encoded Word Extensions: Character Sets, Languages, and Continuations
				This desribes the iMail style of encoding i18n attachment filenames
				Not implimented in A5 
				//test possibly existing support in:                          
				// array imap_mime_header_decode ( string $text )
				// string iconv_mime_decode ( string $encoded_header [, int $mode [, string $charset]] )
				*/
				$result = self::decodeRFC2231Header($partHeaders['content-disposition']);
				if( strlen($result['value']) > 0 )
					$partFilename = $result['value'];
				
			}
			elseif( $mimeType == 'message/rfc822') 
			{

				//if embedded email i.e. no filename then create filename based on position in parent message and try find matching part
				$partFilename = 'embeddedEmail' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.eml';

			}
			
			if( isset($partHeaders['content-type']) ) 
			{
				
				$mimeType = $matches[1];
				$mimeTypeParts = explode('/', $mimeType, 2);
			
				//if is multipart part continue to next part
				if( $mimeTypeParts[0] == 'multipart' )
					continue;
		
				if( false && $mimeType == 'text/plain' && $partFilename == 'unknownfilename')
					$partFilename = 'embeddedtext' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.txt';
				else if( false && $mimeType == 'text/html' && $partFilename == 'unknownfilename')
					$partFilename = 'embeddedhtml' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.html'; 
				else if( $mimeType == "text/calendar" && $partFilename == 'unknownfilename') //else if no filename but still a text/calendar, then create a nice cal filename.ics
					$partFilename = 'calendar' . $args['parentEmbeddedAttachmentPrefix'] . ($embeddedAttachmentPrefix++) . '.ics';
			
			}
			
			//basic checking to not decode an already decoded filename
			if( strpos( $partFilename, '=?') !== false && strpos( $partFilename, '?=') !== false)
			{
				
				$partFilename = iconv_mime_decode($partFilename, 0, "UTF-8");   
				
			}
			
			if( isset($partHeaders['content-id']) ) 
			{
                //most inlines CIDs are images
				$fileExtension = '';
				if( $mimeType == 'image/jpeg' )
					$fileExtension = '.jpg';
				elseif( $mimeType == 'image/gif' )
					$fileExtension = '.gif';
				elseif( $mimeType == 'image/png' )
					$fileExtension = '.png';
				
				if( strpos($partFilename, 'unknownfilename') !== false && isset($partHeaders['content-location']) )
					$partFilename = $partHeaders['content-location'];
				elseif( strlen($fileExtension) > 0 && substr( $partFilename, -4) != $fileExtension )
				{
					//catch .jpeg && ($fileExtension !=)
					if( substr( $partFilename, -5) != '.jpeg' )
						$partFilename .= $fileExtension;	
				}
				
			    if( in_array( $partFilename, $existingPartFilenames ) ) 
				{
					
					//attachment name already used (lazy email clients) so increment the filename before writing it to disk
					//insert an ordinal number number into unknown filenames so attachment fetcher can fetch correct attachment
					$filenamePrependId++;
					$partFilename = (String)$filenamePrependId . $partFilename;
				
				} 
				$existingPartFilenames[] = $partFilename;
				
				$partFilenameFS = makeFSSafe( $folderUTF7 . $args['uniqueId'] . md5($partFilename) );
			    
				if( strpos($partFilename, 'unknownfilename') !== false )
					$partFilename = $args['folder'] . $args['uniqueId'] . md5($partFilename);
				
			}

			if( md5($partFilename) == $args['filename'] )
			{
			    
				$file = array();
				$file['filename'] = $partFilename;
				$file['mimeType'] = $mimeType;
				
				//CONSIDER: would saving to disk at this point save overhead?
				$file['content'] = self::returnDecodedPart($part);
				return true;
			
			} 
			elseif( $mimeType == 'message/rfc822' ) 
			{
    			$inlineMessage = new Atmail_Mail_Message( array('raw' => self::returnDecodedPart($part)) );
		        $childArgs = $args;
				$childArgs['parentEmbeddedAttachmentPrefix'] = $args['parentEmbeddedAttachmentPrefix'] . $embeddedAttachmentPrefix;
				$found = self::recursivelySearchMessageForAttachment($inlineMessage, $childArgs, $file);
				if( $found )
					return true;
			}
			
		}
		return false;
	}
	
	public static function getUniqueAddresses( $listString )
	{
	    
		$listArray = getProcessedRecipientObjects( $listString );
		if( !is_array($listArray) )
		{
			
			return $listString;
			
		}
		require_once('Mail/RFC822.php');
		$rfc822 = new Mail_RFC822;
		$tmp = $unique = array();
		foreach( $listArray as $k => $email )
		{
			
			if( $rfc822->isValidInetAddress($email->mailbox . '@' . $email->host) ) 
			{
					
				if ( !in_array(strtolower($email->mailbox . '@' . $email->host), $tmp) ) 
				{
		        
					$tmp[] = strtolower($email->mailbox . '@' . $email->host);
		            
					if( !empty($email->personalUTF8) )
					{
						
						//strip commas from personal until UI fixed
						$email->personalUTF8 = str_replace(',', '', $email->personalUTF8);
						$email->personalUTF8 = trim($email->personalUTF8, '"');       			    
						$unique[] = "\"" . $email->personalUTF8 . "\" <{$email->mailbox}@{$email->host}>";
						
					}
		
					else if( !empty($email->personal) ) 
					{

       			        //strip commas from personal until UI fixed
						$email->personal = str_replace(',', '', $email->personal);
						$email->personal = trim($email->personal, '"');       			    
						$unique[] = "\"" . $email->personal . "\" <{$email->mailbox}@{$email->host}>";
						
   					}
					else
					{
	                	
						$unique[] = "<{$email->mailbox}@{$email->host}>";
						
					}
					    
				}
			
			}
			
		}   
		unset($tmp);
		$resultString = join(', ', $unique);
		
		return $resultString;
    
	}
	     
}
