<?php
/**
 * ###############################################
 *
 * SWIFT Framework
 * _______________________________________________
 *
 * @author		Varun Shoor
 *
 * @package		SWIFT
 * @copyright	Copyright (c) 2001-2012, Kayako
 * @license		http://www.kayako.com/license
 * @link		http://www.kayako.com
 *
 * ###############################################
 */

/**
 * The IMAP Communication Lib
 *
 * @author Varun Shoor
 */
class SWIFT_MailParserIMAP extends SWIFT_Library {
	/**
	 * Constructor
	 *
	 * @author Varun Shoor
	 * @return bool "true" on Success, "false" otherwise
	 */
	public function __construct() {
		parent::__construct();

		/**
		 * BUG FIX - Ravi Sharma <ravi.sharma@kayako.com>
		 *
		 * SWIFT-4288 Result: description is not being shown in email parser logs in case of POP/IMAP.
		 *
		 * Comments: In case of IMAP/POP system should always use the language from files.
		 */
		$this->Language->Load('emailparser', SWIFT_LanguageEngine::TYPE_FILE);

		return true;
	}

	/**
	 * Destructor
	 *
	 * @author Varun Shoor
	 * @return bool "true" on Success, "false" otherwise
	 */
	public function __destruct() {
		parent::__destruct();

		return true;
	}

	/**
	 * Process the IMAP/POP3 Accounts
	 *
	 * @author Varun Shoor
	 * @return bool "true" on Success, "false" otherwise
	 * @throws SWIFT_Exception If the Class is not Loaded or If a core error is encountered
	 */
	public function Process() {
		if (!$this->GetIsClassLoaded()) {
			throw new SWIFT_Exception(SWIFT_CLASSNOTLOADED);

			return false;
		}

		// We require IMAP.  If it's not installed we can't fetch email through this script anyways.
		if (!extension_loaded('imap')) {
			throw new SWIFT_Exception('Error: Your PHP was not compiled with IMAP support.' . SWIFT_CRLF .
					'*nix users should recompile their PHP with the \'--with-imap\' flag; Windows users can simply uncomment the extension=\'php_imap.dll\' line in their php.ini');

			//clearParserLockFile();
			return false;
		}

		// First get all the queues
		$_emailQueuesContainer = array();

		$_debugMessages = '';

		$this->Database->Query("SELECT * FROM " . TABLE_PREFIX . "emailqueues WHERE isenabled = '1' ORDER BY emailqueueid ASC");
		while ($this->Database->NextRecord()) {
			if ($this->Database->Record['fetchtype'] == SWIFT_EmailQueue::FETCH_PIPE) {
				continue;
			}

			$_emailQueuesContainer[$this->Database->Record['emailqueueid']] = $this->Database->Record;
		}

		if (!count($_emailQueuesContainer)) {
			return false;
		}

		$_rejectedMessageCount = 0;
		$_acceptedMessageCount = 0;
		$_totalMessages = 0;

		$_processingResult = array($_totalMessages, $_acceptedMessageCount, $_rejectedMessageCount);


		foreach ($_emailQueuesContainer as $_key => $_emailQueueContainer) {
			$_isPOP3 = false;

			$_fetchType = false;

			switch ($_emailQueueContainer['fetchtype']) {
				case SWIFT_EmailQueue::FETCH_IMAP: {
						$_fetchType = 'imap/notls';
					}
					break;

				case SWIFT_EmailQueue::FETCH_IMAPSSL: {
						$_fetchType = 'imap/ssl/novalidate-cert/notls';
					}
					break;

				case SWIFT_EmailQueue::FETCH_IMAPTLS: {
						$_fetchType = 'imap/ssl/novalidate-cert';
					}
					break;

				case SWIFT_EmailQueue::FETCH_POP3SSL: {
						$_fetchType = 'pop3/ssl/novalidate-cert/notls';
						$_isPOP3 = true;
					}
					break;

				case SWIFT_EmailQueue::FETCH_POP3TLS: {
						$_fetchType = 'pop3/ssl/novalidate-cert';
						$_isPOP3 = true;
					}
					break;

				default: {
						$_fetchType = 'pop3/notls';
						$_isPOP3 = true;
					}
					break;
			}

			if (!SWIFT::Get('iscron')) {
				echo "Running parser for Queue ID '" . $_emailQueueContainer['emailqueueid'] . "':  " . $_emailQueueContainer['email'] . SWIFT_CRLF;
			}

			$_processingResult = array(false, false, false, false);
			try {
				$_processingResult = $this->ProcessMailbox($_fetchType, $_isPOP3, $_emailQueueContainer);
			} catch (SWIFT_Exception $_SWIFT_ExceptionObject) {
				if (!SWIFT::Get('iscron')) {
					echo $_SWIFT_ExceptionObject->getMessage();
				}
			}

			// array($_totalMessages, $_acceptedMessages, $_rejectedMessages, $_rejectedMessagesContainer);

			// Process the Rejected Messages
			if ( _is_array($_processingResult[3]) )
			{
				foreach ($_processingResult[3] as $_key => $_MailPropertyContainerObject)
				{
					$_subject = '';
					if ( isset($_MailPropertyContainerObject->subject) && $_MailPropertyContainerObject->subject != '' )
					{
						$_subject = $_MailPropertyContainerObject->subject;
					}

					$_from = '';
					if ( isset($_MailPropertyContainerObject->from) && $_MailPropertyContainerObject->from != '' )
					{
						$_from = $_MailPropertyContainerObject->from;
					}

					$_to = '';
					if ( isset($_MailPropertyContainerObject->to) && $_MailPropertyContainerObject->to != '' )
					{
						$_to = $_MailPropertyContainerObject->to;
					}

					$_messageID = '';
					if (isset($_MailPropertyContainerObject->message_id) && $_MailPropertyContainerObject->message_id != '') {
						$_messageID = $_MailPropertyContainerObject->message_id;
					}

					SWIFT_ParserLog::Create(SWIFT_ParserLog::TYPE_FAILURE, 0, 0, $_subject,
					$_from, $_to, $_MailPropertyContainerObject->size,
					sprintf($this->Language->Get('errtoobig'), $this->Settings->Get('pr_sizelimit') . $this->Language->Get('kb'),
					sprintf("%.02f", ($_MailPropertyContainerObject->size)/1024) . $this->Language->Get('kb')), '', 0, array(), $_messageID);
				}
			}

			if (!SWIFT::Get('iscron')) {
				echo 'Queue Total: ' . $_processingResult[0] . ', Accepted: ' . $_processingResult[1] . ', Rejected: ' . $_processingResult[2] . SWIFT_CRLF;
			}

			$_acceptedMessageCount += $_processingResult[1];
			$_rejectedMessageCount += $_processingResult[2];
			$_totalMessages += $_processingResult[0];
		}

		if (!SWIFT::Get('iscron')) {
			echo 'Final Total: ' . $_totalMessages . ', Accepted: ' . $_acceptedMessageCount . ', Rejected: ' . $_rejectedMessageCount . SWIFT_CRLF;
		}

		return $_debugMessages;
	}

	/**
	 * Process the given mailbox
	 *
	 * @author Varun Shoor
	 * @param string $_fetchType The Fetch Type String
	 * @param bool $_isPOP3 Whether this is a POP3 mailbox
	 * @param array $_emailQueueContainer The Email Queue Container
	 * @return array The Result Array on Success, "false" otherwise
	 * @throws SWIFT_Exception If the Class is not Loaded or If an error is encountered
	 */
	protected function ProcessMailbox($_fetchType, $_isPOP3, $_emailQueueContainer) {
		if (!$this->GetIsClassLoaded()) {
			throw new SWIFT_Exception(SWIFT_CLASSNOTLOADED);

			return false;
		}


		$_mailboxPointer = @imap_open('{' . $_emailQueueContainer['host'] . ':' . $_emailQueueContainer['port'] . '/' . $_fetchType . '}INBOX',
				$_emailQueueContainer['username'], $_emailQueueContainer['userpassword']);
		$_totalMessages = 0;
		$_acceptedMessages = 0;
		$_rejectedMessages = 0;
		$_rejectedMessagesContainer = array();

		$_maxMessages = intval($this->Settings->Get('pr_procno'));

		if ($_mailboxPointer) {
			// remove "mailbox is empty" notices from the beginning of a queue behavior when a later box is broken
			imap_errors();

			$_imapMessageCount = imap_num_msg($_mailboxPointer);
			// Get the Message List. We only process unread messages if leave a copy on server setting is true
			$_messageList = $_messagePropertiesContainer = array();

			if ($_imapMessageCount != '0')
			{
				if ($_emailQueueContainer['leavecopyonserver'] == '1') {
					$_messageList = imap_search($_mailboxPointer, 'UNSEEN', SE_UID);
				} else {
					$_messageList = imap_search($_mailboxPointer, 'ALL', SE_UID);
				}
			}

			if (!_is_array($_messageList)) {
				$_totalMessages = 0;
			} else {
				$_totalMessages = count($_messageList);
			}

			// Mailbox empty?
			if ($_totalMessages == 0) {
				return array(0, 0, 0, $_rejectedMessagesContainer);
			}

			$_processMessages = 0;
			$_newMessageList = array();

			for($i=0;$i<$_totalMessages;$i++) {
				if ($_processMessages >= $_maxMessages) {
					break;
				}
				$_newMessageList[] = $_messageList[$i];
				$_processMessages++;
			}

			// Fetch the message properties
			$_messageOverviewContainer = array();
			if (_is_array($_newMessageList)) {
				$_messageOverviewContainer = imap_fetch_overview($_mailboxPointer, implode(',', $_newMessageList), FT_UID);

				foreach ($_messageOverviewContainer as $_key => $_val) {
					$_messagePropertiesContainer[$_val->uid] = $_val;
				}
			}

			// Attempt to set time limit
			@set_time_limit(0);
			foreach ($_newMessageList as $_key => $_messageID) {
				$_messageUID = $_messageID;

				//$_messageUID = imap_uid($_mailboxPointer, $_messageID);
				$_messageNumber = imap_msgno($_mailboxPointer, $_messageID);

				$_messagePropertyContainer = array();
				if (isset($_messagePropertiesContainer[$_messageID])) {
					$_messagePropertyContainer = $_messagePropertiesContainer[$_messageID];
				}

				$_uidType = FT_UID;

				// There is a limit on how many messages we are to process at once and we have reached it; bail.
				if ($_acceptedMessages >= $_maxMessages) {
					break;
				}

				// Check size limit
				if (isset($_messagePropertyContainer->size) && ($_messagePropertyContainer->size/1024) > $this->Settings->Get('pr_sizelimit')) {
					$_rejectedMessagesContainer[$_messageID] = $_messagePropertyContainer;

					if (isset($_messagePropertyContainer->message_id) && SWIFT_ParserLog::IsMessageIDExist($_messagePropertyContainer->message_id)) {
						$_rejectedMessages++;
						continue;
					}

					/**
					 * BUG FIX - Ravi Sharma <ravi.sharma@kayako.com>
					 *
					 * SWIFT-4619 Warn users when their email is rejected due to size limit.
					 */
					imap_setflag_full($_mailboxPointer, $_messageUID, "\\Seen", $_uidType);
					$_mailHeaders = imap_fetchheader($_mailboxPointer, $_messageUID, $_uidType);
					preg_match('/From:\s*(.*[^\s])\s*<\s*(.*[^\s])\s*>/', $_mailHeaders, $_matches);

					if (IsEmailValid(end($_matches))) {
						$this->Load->Library('Mail:Mail');

						// Load the phrases from the database..
						$this->Language->Load('emailparser', SWIFT_LanguageEngine::TYPE_FILE);
						$this->Language->Load('ticketemails', SWIFT_LanguageEngine::TYPE_DB);

						$_textEmailContents = sprintf($this->Template->Get('email_sizelimit_text', SWIFT_TemplateEngine::TYPE_DB), $this->Settings->Get('pr_sizelimit'));
						$_htmlEmailContents = sprintf($this->Template->Get('email_sizelimit_html', SWIFT_TemplateEngine::TYPE_DB), $this->Settings->Get('pr_sizelimit'));

						$this->Mail->SetFromField($_emailQueueContainer['email'], SWIFT::Get('companyname'));
						$this->Mail->SetToField(end($_matches));
						$this->Mail->SetSubjectField($this->Language->Get('sizelimitrejectsub'));

						$this->Mail->SetDataText($_textEmailContents);
						$this->Mail->SetDataHTML($_htmlEmailContents);

						$this->Mail->SendMail(false, $_emailQueueContainer['emailqueueid']);
					}

					$_rejectedMessages++;
					continue;
				}

				// Process the Message
				$_mailHeaders = imap_fetchheader($_mailboxPointer, $_messageUID, $_uidType);
				$_mailHeaders = preg_replace("#(\r\n|\r|\n)#s", SWIFT_CRLF, $_mailHeaders);

				$_finalMailData = $_mailHeaders . preg_replace("#(\r\n|\r|\n)#s", SWIFT_CRLF, imap_body($_mailboxPointer, $_messageUID, $_uidType));

				$_acceptedMessages++;

				$_SWIFT_MailParserObject = new SWIFT_MailParser($_finalMailData);

				$_SWIFT_EmailQueueObjectDispatch = null;
				if (!isset($_emailQueueContainer['forcequeue']) || $_emailQueueContainer['forcequeue'] == '1')
				{
					$_SWIFT_EmailQueueObjectDispatch = SWIFT_EmailQueue::RetrieveStore($_emailQueueContainer);
				}

				try {
					$_SWIFT_MailParserObject->Process(false, $_SWIFT_EmailQueueObjectDispatch);
				} catch (Exception $Exception) {
					$this->Log->Log('Failed to process message with ID: ' . $_messageID . ' from IMAP Email Queue: ' . $_emailQueueContainer['host'], SWIFT_Log::TYPE_ERROR, 'SWIFT_MailParserIMAP');
				}

				// Delete the message only if leave copy on server is set to false..the seen flag is set by default
				if ($_emailQueueContainer['leavecopyonserver'] != '1') {
					imap_delete($_mailboxPointer, $_messageUID, $_uidType);
				} else {
					imap_setflag_full($_mailboxPointer, $_messageUID, '\Seen');
				}

			}

			// remove "Undisclosed-Recipient;. (errflg=3)" notices before closing imap
			imap_errors();

			imap_expunge($_mailboxPointer);
			imap_close($_mailboxPointer);


			return array($_totalMessages, $_acceptedMessages, $_rejectedMessages, $_rejectedMessagesContainer);

		} else {

			$_imapErrors = '---------------------------------------------------' . SWIFT_CRLF;
			$_errorIndex = 1;

			foreach(imap_errors() as $_errorMessage) {
				$_imapErrors .= $_errorIndex . '. ' . $_errorMessage . SWIFT_CRLF;

				$_errorIndex++;
			}


			$_imapErrors .= '---------------------------------------------------' . SWIFT_CRLF;

			throw new SWIFT_Exception('One or more errors occurred when reading Queue #' . $_emailQueueContainer['emailqueueid'] . SWIFT_CRLF . $_imapErrors);
		}

		return false;
	}
}
?>