<?php
/*
 * e107 website system
 *
 * Copyright (C) 2008-2009 e107 Inc (e107.org)
 * Released under the terms and conditions of the
 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
 *
 * Bounce handler for cron or manual triggering
 *
 * $Source: /cvs_backup/e107_0.8/e107_handlers/pop_bounce_handler.php,v $
 * $Revision$
 * $Date$
 * $Author$
*/

/*
This handler is intended for use with a cron job, or with manual triggering, to get bounce notifications from a POP3 or IMAP account.
It extends the receiveMail class (pop3_class.php) to provide some higher level methods.
Can be used for getting mail from any POP3 mail account.

Some parts used from/based on the receiveMail class, by Mitul Koradia (Email: mitulkoradia@gmail.com, Cell : +91 9879697592)

Notes:
1. Requires that the IMAP extension is installed
2. Mailbox names that contain international characters besides those in the printable ASCII space have to be encoded width imap_utf7_encode(). 
*/


/**
 *
 */
class pop3BounceHandler
{
	protected	$e107;

	protected $server='';				// String to connect to server
	protected $username='';
	protected $password='';

	protected $email='';				// Email address receiving bounces
	protected $delBounce = FALSE;		// TRUE to delete emails after reading

	protected $mailResource = '';		// Resource identifier for IMAP
	
	protected $mailManager = FALSE;


	/**
	 * @param $override
	 */
	public function __construct($override = FALSE)
	{
		global $pref;
		$this->e107 = e107::getInstance();
		if (($override === FALSE) || !is_array($override))
		{	// Set up from prefs
			$override['mail_bounce_user'] = $pref['mail_bounce_user'];
			$override['mail_bounce_pass'] = $pref['mail_bounce_pass'];
			$override['mail_bounce_email'] = $pref['mail_bounce_email'];
			$override['mail_bounce_pop3'] = $pref['mail_bounce_pop3'];
			$override['mail_bounce_type'] = varset($pref['mail_bounce_type'],'pop3');
		}

		if($override['mail_bounce_type']=='imap')
		{
			$port = varset($override['mail_bounce_port'], '143');
			$strConnect='{'.$override['mail_bounce_pop3'].':'.$port. '}INBOX';
		}
		else
		{
			$port = varset($override['mail_bounce_port'], '110');		// POP3 port
			$servertype = '/'.varset($override['mail_bounce_type'], 'pop3');
			$strConnect='{'.$override['mail_bounce_pop3'].':'.$port. $servertype.'}INBOX';
		}
		$this->server			=	$strConnect;
		$this->username			=	$override['mail_bounce_user'];
		$this->password			=	$override['mail_bounce_pass'];
		$this->email			=	$override['mail_bounce_email'];

		$this->delBounce = ($pref['mail_bounce_delete']) ? true : false;
	}


	/**
	 * @return void
	 */
	function connect() //Connect To the Mail Box
	{
		$this->mailResource=imap_open($this->server,$this->username,$this->password);
	}


	/**
	 * @return int
	 */
	function getTotalMails() //Get Total Number off Unread Email In Mailbox
	{
		$headers=imap_headers($this->mailResource);
		return count($headers);
	}


	/**
	 * @param $mid
	 * @return array
	 */
	function getHeaders($mid) // Get Header info
	{
		$mail_header=imap_header($this->mailResource,$mid);
		$sender=$mail_header->from[0];
		$sender_replyto=$mail_header->reply_to[0];
		$stat = !(strtolower($sender->mailbox) != 'mailer-daemon' && strtolower($sender->mailbox) != 'postmaster');
        if(strpos($mail_header->subject,"delayed"))
		{
			$stat = FALSE;
		}
			$mail_details=array(
					'from'=>strtolower($sender->mailbox).'@'.$sender->host,
					'fromName'=>$sender->personal,
					'toOth'=>strtolower($sender_replyto->mailbox).'@'.$sender_replyto->host,
					'toNameOth'=>$sender_replyto->personal,
					'subject'=>$mail_header->subject,
					'to'=>strtolower($mail_header->toaddress),
					'bounce'=>$stat,
					'date'=>$mail_header->date
				);
		return $mail_details;
	}


	/**
	 * @param $structure
	 * @return string
	 */
	protected function get_mime_type(&$structure) //Get Mime type Internal Private Use
	{
		$primary_mime_type = array("TEXT", "MULTIPART", "MESSAGE", "APPLICATION", "AUDIO", "IMAGE", "VIDEO", "OTHER");

		if($structure->subtype) 
		{
			return $primary_mime_type[(int) $structure->type] . '/' . $structure->subtype;
		}
		return "TEXT/PLAIN";
	}


	/**
	 * @param $stream
	 * @param $msg_number
	 * @param $mime_type
	 * @param $structure
	 * @param $part_number
	 * @return false|string
	 */
	protected function get_part($stream, $msg_number, $mime_type, $structure = false, $part_number = false) //Get Part Of Message Internal Private Use
	{
		if(!$structure) 
		{
			$structure = imap_fetchstructure($stream, $msg_number);
		}
		if($structure) 
		{
			if($mime_type == $this->get_mime_type($structure))
			{
				if(!$part_number)
				{
					$part_number = '1';
				}
				$text = imap_fetchbody($stream, $msg_number, $part_number);
				if($structure->encoding == 3)
				{
					return imap_base64($text);
				}
				else if($structure->encoding == 4)
				{
					return imap_qprint($text);
				}
				else
				{
					return $text;
				}
			}
			if($structure->type == 1) /* multipart */
			{
				foreach($structure->parts as $index => $sub_structure)
				{
					if($part_number)
					{
						$prefix = $part_number . '.';
					}
					$data = $this->get_part($stream, $msg_number, $mime_type, $sub_structure, $prefix . ($index + 1));
					if($data)
					{
						return $data;
					}
				}
			}
		}
		return false;
	}


	/**
	 * @param $mid
	 * @param $mode
	 * @return false|string
	 */
	function getBody($mid, $mode='') // Get Message Body
	{
		if($mode != 'plain')
		{
			$body = $this->get_part($this->mailResource, $mid, 'TEXT/HTML');
		}
		if (($body == '') || $mode == 'plain')
		{
			$body = $this->get_part($this->mailResource, $mid, 'TEXT/PLAIN');
		}
		if ($body == '') 
		{
			return '';
		}
		return $body;
	}


	/**
	 * @param $mid
	 * @return void
	 */
	function deleteMails($mid) // Delete That Mail
	{
		imap_delete($this->mailResource,$mid);
	}


	/**
	 * @return void
	 */
	function close_mailbox() //Close Mail Box
	{
		imap_close($this->mailResource,CL_EXPUNGE);
	}



	/**
	 * Loop reading all emails from the bounces mailbox. If an email address and/or e107 header are
	 * identified, process the bounce.
	 * Delete all emails after processing, if $pref flag is set.
	 *
	 * @return void
	 */
	public function processBounces()
	{
		$identifier = deftrue('MAIL_IDENTIFIER', 'X-e107-id');
		
		$this->connect();
		$tot = $this->getTotalMails();		// Get all the emails
		for ($i = 1; $i <= $tot; $i++)
		{
			$head = $this->getHeaders($i);	// Get the headers
			if ($head['bounce'])
			{
				//print_a($head);
				$body = $this->getBody($i);
				$e107Header = '';
				if (preg_match('#.*'.$identifier.':(.*?)[\n\r]#',$body, $result))
				{
					$e107Header = varset($result[1],'');
				}
				$emailAddress = '';
				if (preg_match("/[\._a-zA-Z0-9-]+@[\._a-zA-Z0-9-]+/i",$body,$result))
				{
					$emailAddress = varset($result[0],'');
					if ($emailAddress == $this->email)
					{
						$emailAddress = '';			// Email address found is that of the bounce email account
					}
				}
				// Call the bounce handler here
				if ($this->mailManager === FALSE)
				{
					require_once(e_HANDLER.'mail_manager_class.php');
					$this->mailManager = new e107MailManager();
				}
				echo "Email: {$emailAddress}<br />Header: {$e107Header}<br />";
				$this->mailManager->markBounce($e107Header, $emailAddress);
			}
			// Now delete the email, if option set (do it regardless of whether a bounce email)
			if ($this->delBounce)
			{
				$this->deleteMails($i);		// Not actually deleted until we close the mailbox
			}
		}
		$this->close_mailbox();
	}
}



