<?php
/**
* Quarantine lib
* @author Samuel Tran <stran2005@users.sourceforge.net>
* @author Brian Wong <bwsource@users.sourceforge.net>
* @author Nicolas Peyrussie <peyrouz@users.sourceforge.net>
* @version 04-03-07
*
* Copyright (C) 2005 - 2007 MailZu
* License: GPL, see LICENSE
*/
/**
* Base directory of application
*/
@define('BASE_DIR', dirname(__FILE__) . '/..');
/**
* CmnFns class
*/
include_once('CmnFns.class.php');
/**
* Include AmavisdEngine class
*/
include_once('lib/AmavisdEngine.class.php');
/**
* PHPMailer
*/
include_once('lib/PHPMailer.class.php');

/**
* Provide quarantine related functions
*/

/**
* Release messages function
* @param array $emailaddresses recipient email address(es)
* @param array $mail_id_array containing mail_id of messages to be released
* @result return array of messages whose release failed 
*/
function releaseMessages($emailaddresses, $mail_id_array) {

	/*** Array pertaining to the release of messages ***/
	// This is an array of array, the key being the $mail_id
	// and the value being an array containing all the messages info (time, subject, ...) and also the release status.
	// The reason for this is that we want to keep the ordering of the messages selected for release.
	$release_messages = array();
	// This is an array of array, the key being the host
	// and the value being an array containing all the release info such as secret_id (one row per message)
	$hosts = array();

	/*** Variables pertaining to the request of release ***/
	// This array contains the messages that the logged in user wants the Admins to release
	$release_req_messages = array();
	// Counter for the number of release requests
	$j = 0;

	$nb_failure = 0;

	$db = new DBEngine();

	// Set autocommit to false to improve speed of 'RS' flag set up
	$result = $db->db->autoCommit(false);
	$db->check_for_error($result, 'PEAR DB autoCommit(false)');

	// Fill the arrays	
	foreach ($mail_id_array as $mail_id_recip) {

		// Get mail_id and recipient email address
		$temp = preg_split('/_/', $mail_id_recip, 2);
		$mail_id = $temp[0];
		$recip_email = $temp[1];

		// Check if logged in user is admin or logged in user is trying to release his own messages
		if ( Auth::isMailAdmin() || Auth::isDomainAdmin() || in_array($recip_email, $emailaddresses) )
			$result = $db->get_message($recip_email, $mail_id);
		else
			continue;

		$rs = $result[0];

		// if content type is 'B' or 'V' and the logged in user is not admin
		// add message to array of release request
		if ( in_array($rs['content'], array( 'B', 'V')) && (! Auth::isMailAdmin() && ! Auth::isDomainAdmin()) ) {
			$release_req_messages[ $j ] = array(
				"mail_id" => $mail_id,
				"from_addr" => $rs[ 'from_addr' ],
				"subject" => $rs[ 'subject' ],
				"time_num" => $rs[ 'time_num' ],
				"spam_level" => $rs[ 'spam_level' ],
				"content" => $rs['content']
			);

			// Try to update the RS flag to 'p' for pending
			if ( ! $db->update_msgrcpt_rs($mail_id, $recip_email, 'p') ) {
				$release_req_messages[ $j ]["status"] = "Error: " . $db->get_err();
			} else {
				$release_req_messages[ $j ]["status"] = "Pending";
			}

			$j++;

		// Other cases where:
		//	- content type is 'B' or 'V' but the logged in user is admin, therefore allowed to release message
		//	- content type is 'S' or 'H'	
		} else {
			// add message to be released to $hosts array
			$release_messages[ $mail_id_recip ] = array(
				"mail_id" => $mail_id,
				"time" => $rs['time_num'],
				"subject" => $rs['subject'],
				"from_addr" => $rs['from_addr'],
				"spam_level" => $rs['spam_level'],
				"content" => $rs['content'],
			);
			$hosts[ $rs['host'] ][ $mail_id_recip ] = array(
				"secret_id" => $rs['secret_id'],
				"quar_type" => $rs['quar_type'],
				"quar_loc" => $rs['quar_loc'],
				"recip_email" => $rs['email']
			);
		}
	}

	global $conf;

	// If release request needs to be sent to Admins
	if ( is_array($release_req_messages) && !empty($release_req_messages) 
	  && $conf['app']['notifyAdmin'] )
		sendMailToAdmin(translate('Request release'), $release_req_messages);

	// If release needs to be done
	if ( is_array($hosts) && !empty($hosts) ) {

		// For each host create socket, connect and release all messages pertaining to that host	
		foreach ($hosts as $host => $message_info ) {

			// Create new TCP/IP socket and try to connect to $host using this socket
			$am = new AmavisdEngine($host);

			if ( ! $am->connected )
				foreach ($message_info as $mail_id_recip => $release_info) {
					$release_messages[ $mail_id_recip ][ 'error_code' ] = 1;
					$release_messages[ $mail_id_recip ][ 'status' ] = $am->last_error;
					$nb_failure++;
				}
			else { 
				foreach ($message_info as $mail_id_recip => $release_info) {
					$socket_binding_result 	=	$am->release_message(
						$release_messages[ $mail_id_recip ][ 'mail_id' ],
						$release_info[ 'secret_id' ],
						$release_info[ 'recip_email' ],
						$release_info[ 'quar_type' ],
						$release_info[ 'quar_loc' ]
					);

					if (preg_match('/^setreply=250/', $socket_binding_result)) {
						if ( $db->update_msgrcpt_rs($release_messages[ $mail_id_recip ][ 'mail_id' ], $release_info[ 'recip_email' ], 'R') ) {
							$release_messages[ $mail_id_recip ][ 'error_code' ] = "0";
							CmnFns::write_log('Message Released [' . $release_messages[ $mail_id_recip ][ 'content' ] . ']: '
								. $release_messages[ $mail_id_recip ][ 'mail_id' ], $_SESSION['sessionID']);
						} else {
							$release_messages[ $mail_id_recip ][ 'error_code' ] = 2;
							$release_messages[ $mail_id_recip ][ 'status' ] = "Error: " . $db->get_err();
							$nb_failure++;
						}
					} else {
						$release_messages[ $mail_id_recip ][ 'error_code' ] = 3;
						$release_messages[ $mail_id_recip ][ 'status' ] = $am->last_error;
						$nb_failure++;
					}
				}

				// Shuting down and closing socket
				$am->disconnect();
			}
		}

	}

	// Commit, then set autocommit back to true
	$result = $db->db->commit();
	$db->check_for_error($result, 'PEAR DB commit()');
	$result = $db->db->autoCommit(true);
	$db->check_for_error($result, 'PEAR DB autoCommit(true)');

	// Build array of messages whose release failed
	$failed_array = array();
	$i = 0;

	if ( $nb_failure > 0 ) {
	
		foreach ($mail_id_array as $mail_id_recip) {

			if ($release_messages[ $mail_id_recip ][ 'error_code' ] != 0) {
				$failed_array[ $i ] = array(
					"mail_id" => $release_messages[ $mail_id_recip ][ 'mail_id' ],
					"from_addr" => $release_messages[ $mail_id_recip ][ 'from_addr' ],
					"subject" => $release_messages[ $mail_id_recip ][ 'subject' ],
					"time_num" => $release_messages[ $mail_id_recip ][ 'time' ],
					"spam_level" => $release_messages[ $mail_id_recip ][ 'spam_level' ],
					"content" => $release_messages[ $mail_id_recip ][ 'content' ],
					"status" => $release_messages[ $mail_id_recip ][ 'status' ]
				);
				CmnFns::write_log($release_messages[ $mail_id_recip ][ 'status' ], $_SESSION['sessionID']);
				$i++;
			}

		}
	}

	// Return array of messages whose release failed
	return $failed_array;

}

/**
* Update messages function
* @param string $content_type 'B', 'S', ...
* @param array $emailaddresses recipient email address(es)
* @param array $mail_id_array containing mail_id of messages to be deleted
* @param bool $all false (default) or true, if true all messages will be deleted
* @result return array of messages whose release failed 
*/
function updateMessages($flag, $content_type, $emailaddresses, $mail_id_array, $all = false) {

	$result_array = array();
	$db = new DBEngine();

	// Set autocommit to false to improve speed of $flag set
	$result = $db->db->autoCommit(false);
	$db->check_for_error($result, 'PEAR DB autoCommit(false)');

	if ( $all ) {
		$res = $db->get_user_messages($content_type, $emailaddresses, 'msgs.time_num', 'DESC', '', false, 0, 0, true);
		for ($i = 0; is_array($res) && $i < count($res); $i++) {
			$rs = $res[$i];

			if ( Auth::isMailAdmin() || Auth::isDomainAdmin() || in_array($rs['email'], $emailaddresses) ) {
				if ( ! $db->update_msgrcpt_rs($rs['mail_id'], $rs['email'], $flag ) ) {
					$rs = $result[0];
					$result_array[ $i ] = array(
						"mail_id" => $mail_id,
						"from_addr" => $rs[ 'from_addr' ],
						"subject" => $rs[ 'subject' ],
						"time_num" => $rs[ 'time_num' ],
						"spam_level" => $rs[ 'spam_level' ],
						"status" => "Error: " . $db->get_err()
					);
				}
			} else {
				continue;
			}
		}

	} else {

		$i = 0;

		foreach ($mail_id_array as $mail_id_recip) {
		
			// Get mail_id and recipient email address
			$temp = preg_split('/_/', $mail_id_recip, 2);
			$mail_id = $temp[0];
			$recip_email = $temp[1];

			// Check if logged in user is admin or logged in user is trying to delete his own messages
			if ( Auth::isMailAdmin() || Auth::isDomainAdmin() || in_array($recip_email, $emailaddresses) ) {
				$result = $db->get_message($recip_email, $mail_id);
			} else {
				continue;
			}

			if ( ! $db->update_msgrcpt_rs($mail_id, $recip_email, $flag) ) {
				$rs = $result[0];
				$result_array[ $i ] = array(
					"mail_id" => $mail_id,
					"from_addr" => $rs[ 'from_addr' ],
					"subject" => $rs[ 'subject' ],
					"time_num" => $rs[ 'time_num' ],
					"spam_level" => $rs[ 'spam_level' ],
					"status" => "Error: " . $db->get_err()
				);
			$i++;
			}
		}
	}

	// Commit, then set autocommit back to true
	$result = $db->db->commit();
	$db->check_for_error($result, 'PEAR DB commit()');
	$result = $db->db->autoCommit(true);
	$db->check_for_error($result, 'PEAR DB autoCommit(true)');

	// Return array of messages whose release failed
	return $result_array;
}

/**
* @author Mikko Husari <husku@husku.net>
* Update white/blaclist function
* @param string $flag 'W', 'B'
* @param array $emailaddresses recipient email address(es)
* @param array $mail_id_array containing mail_id of messages to be whitelisted
* @result return array of messages whose release failed 
*/
function updateWblist($flag, $emailaddresses, $mail_id_array, $action) {

	$result_array = array();
	$db = new DBEngine();

	$i = 0;

	foreach ($mail_id_array as $mail_id_recip) {
	
		// Get mail_id and recipient email address
		$temp = preg_split('/_/', $mail_id_recip, 2);
		$mail_id = $temp[0];
		$recip_email = $temp[1];

		// Check if logged in user is admin or logged in user is trying to delete his own messages
		if ( Auth::isMailAdmin() || in_array($recip_email, $emailaddresses) ) {
			switch ($action) {
				case translate('Whitelist'):
				case translate('Blacklist'):
				case translate('Whitelist by strict addressing'):
				case translate('Blacklist by strict addressing'):
						$rid = $db->mail2userid($recip_email);
						$sid = $db->mailid2sid($mail_id);
						$db->wbinsert($flag, $rid, $sid);
					break;
				case translate('Whitelist by sender domain'):
				case translate('Blacklist by sender domain'):
						$rid = $db->mail2userid($recip_email);
						$sid = $db->mailid2sid($mail_id, true );
						$db->wbinsert($flag, $rid, $sid);
					break;
				case translate('Whitelist by recipient domain'):
				case translate('Blacklist by recipient domain'):
						$rid = $db->mail2userid($recip_email, true);
						$sid = $db->mailid2sid($mail_id);
						$db->wbinsert($flag, $rid, $sid);
					break;
				case translate('Whitelist by both domains'):
				case translate('Blacklist by both domains'):
						$rid = $db->mail2userid($recip_email, true);
						$sid = $db->mailid2sid($mail_id, true);
						$db->wbinsert($flag, $rid, $sid);
					break;
			}
	
		} else {
			continue;
		}
		
	}

	// Return array of messages whose release failed
	return $result_array;
}

/**
* Function that sends:
* 	- an error report if $action = 'Release', 'Delete' or 'Delete All'
*	- an request if $action = 'Request release'
* to $conf['app']['adminEmail']
* @param string $action 'Release', 'Delete', 'Delete All', 'Request release'
* @param $messages_array array containing messages info
*/
function sendMailToAdmin($myaction, $messages_array) {

    	global $conf;
	$title = $conf['app']['title'];
    	$adminEmail = $conf['app']['adminEmail'];

	$sub = "[" . $title . "] Notification from '" . $_SESSION['sessionID'] . "'";
	$msg = "Mail notification sent by '" . $_SESSION['sessionID'] . "' <" . $_SESSION['sessionMail'][0] . ">.\r\n\r\n";

	switch ( $myaction ) {
		case translate('Release'):
		case translate('Release/Request release'):
			$msg .= translate('A problem occured when trying to release the following messages') . ":\r\n\r\n";
			break;
		case translate('Request release'):
			$msg .= translate('Please release the following messages') . ":\r\n\r\n";
			break;
		case translate('Delete'):
		case translate('Delete All'):
			$msg .= translate('A problem occured when trying to delete the following messages') . ":\r\n\r\n";
			break;
		default:
			CmnFns::do_error_box(translate('Unknown action type'), '');
	}

	for ($i = 0; is_array($messages_array) && $i < count($messages_array); $i++) {
		$rs = $messages_array[$i];
		$msg .= "Message #" . ($i+1) . ":\r\n";
		$msg .= "\tmail_id = " . $rs['mail_id'] . "\r\n";
		$msg .= "\t" . translate('From') . " = " . $rs['from_addr'] . "\r\n";
		$msg .= "\t" . translate('Subject') . " = " . $rs['subject'] . "\r\n";
		$msg .= "\t" . translate('Date') . " = " . CmnFns::formatDateTime($rs['time_num']) . "\r\n";
		$msg .= "\t" . translate('Score') . " = " . $rs['spam_level'] . "\r\n";
		$msg .= "\t" . translate('Status') . " = " . $rs['status'] . "\r\n";
		$msg .= "\t" . translate('Content Type') . " = " . $rs['content'] . "\r\n\r\n";

	}

	// Send email
	$mailer = new PHPMailer();
	if ( is_array($adminEmail) ) {
		foreach ($adminEmail as $email) {
			$mailer->AddAddress($email, '');
		}
	} else {
		$mailer->AddAddress($adminEmail, '');
	}
	$mailer->FromName = $_SESSION['sessionID'];
	$mailer->From = $_SESSION['sessionMail'][0];
	$mailer->Subject = $sub;
	$mailer->Body = $msg;
	$mailer->Send();

	return true;

}
?>