<?php
/*
	postfix.inc
	part of the Postfix package for pfSense
	Copyright (C) 2010 Erik Fonnesbeck
	Copyright (C) 2011-2014 Marcello Coutinho

	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

	1. Redistributions of source code MUST retain the above copyright notice,
	   this list of conditions and the following disclaimer.

	2. Redistributions in binary form must reproduce the above copyright
	   notice, this list of conditions and the following disclaimer in the
	   documentation and/or other materials provided with the distribution.

	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

*/
$shortcut_section = "postfix";
require_once("util.inc");
require_once("functions.inc");
require_once("pkg-utils.inc");
require_once("globals.inc");

$pfs_version = substr(trim(file_get_contents("/etc/version")),0,3);
if (is_dir('/usr/pbi/postfix-' . php_uname("m"))) {
	define('POSTFIX_LOCALBASE', '/usr/pbi/postfix-' . php_uname("m"));
} else {
	define('POSTFIX_LOCALBASE','/usr/local');
}

$uname=posix_uname();
if ($uname['machine']=='amd64')
        ini_set('memory_limit', '250M');

function px_text_area_decode($text){
	return preg_replace('/\r\n/', "\n",base64_decode($text));
}

function px_get_real_interface_address($iface) {
	global $config;
	$iface = convert_friendly_interface_to_real_interface_name($iface);
	$line = trim(shell_exec("ifconfig $iface | grep inet | grep -v inet6"));
	$postfix_enabled=$config['installedpackages']['postfix']['config'][0]['enable_postfix'];
	list($dummy, $ip, $dummy2, $netmask) = explode(" ", $line);
	return array($ip, long2ip(hexdec($netmask)));
}

function sync_relay_recipients($via_cron="cron"){
	global $config,$g;
	#relay recipients
	if ($config['installedpackages']['postfixrecipients']['config']) {
		$relay_recipients="";
		$relay_ldap_recipients="";
		$ad_export= "/usr/local/bin/adexport.pl";
		$postfix_enabled=$config['installedpackages']['postfix']['config'][0]['enable_postfix'];
		if (is_array($config['installedpackages']['postfixrecipients']['config'])) {
			$relay_ldap_recipients="";
			$postfix_recipients_config=$config['installedpackages']['postfixrecipients']['config'][0];
			 if($postfix_recipients_config['enable_url'] && is_URL($postfix_recipients_config['custom_url'])){
				print "extracting from ".$postfix_recipients_config['custom_url']."...";
				$relay_recipients .= file_get_contents($postfix_recipients_config['custom_url']);
				print "(". count(file($postfix_recipients_config['custom_url'])).")\n";
			}
			if($postfix_recipients_config['custom_recipients'])
				$relay_recipients .= px_text_area_decode($postfix_recipients_config['custom_recipients']);
			if($postfix_recipients_config['enable_ldap']){
				#validate cront job
				if ($via_cron == "gui"){
					#running via pfsense gui, not time for ldap fetch.
					$ldap_recipients= POSTFIX_LOCALBASE. '/etc/postfix/relay_ldap_recipients.txt';
					if (!file_exists($ldap_recipients))
						system('/usr/bin/touch '. $ldap_recipients);
					$relay_ldap_recipients=file_get_contents($ldap_recipients);
					}
				else{
					#running via crontab, time to get ldap content.
					$ldap_temp=array();
					foreach ($postfix_recipients_config['row'] as $postfix_ldap) {
						print "extracting from ".$postfix_ldap['dc']."...";
						$filename=POSTFIX_LOCALBASE."/etc/postfix/relay_ldap_recipients.".$postfix_ldap['dc'].".txt";
						exec($ad_export." ".$postfix_ldap['dc']." ".$postfix_ldap['cn']." ".$postfix_ldap['username']." ".$postfix_ldap['password'],$ldap_fetch,$status);
						if ($status == 0){
							#write backup conf for ldap server
							$fp=fopen($filename,"w+");
							foreach($ldap_fetch as $key => $value)
									fwrite($fp,$value."\n");
							fclose($fp);
							}
						else{
							if (file_exists($filename)) {
								#LDAP fetch failed...read backup file.
								print "Restoring backup file for ".$postfix_ldap['dc']."...";
								$ldap_fetch=file($filename);
								}
							else{
								#we never got any info from this server.
								print "There is no backup file for ".$postfix_ldap['dc']."...";
								$ldap_fetch=array();
								}
							}
						$ldap_all = array_merge($ldap_temp,$ldap_fetch);
						$ldap_temp=$ldap_all;
						print "(".count($ldap_fetch).")\n";
						$ldap_fetch=array();
						}
						$ldap_unique=array_unique($ldap_all);
						print "Total ldap recipients:".count($ldap_all)."\tunique:".count($ldap_unique)."\n";
						foreach($ldap_unique as $recipient)
							$relay_ldap_recipients.=($recipient != ""?preg_replace("/\s+/","",$recipient)." OK\n":"");

					#save ldap relay recipients
					file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/relay_ldap_recipients.txt",$relay_ldap_recipients, LOCK_EX);
					}
				}
		}
	#save all relay recipients, remove duplicates and reload postfix
	$recipients_file=POSTFIX_LOCALBASE."/etc/postfix/relay_recipients";
	file_put_contents($recipients_file.".unsort",$relay_ldap_recipients."\n".$relay_recipients, LOCK_EX);
	exec('/usr/bin/sort -u '.$recipients_file.'.unsort > '.$recipients_file);
	unlink_if_exists($recipients_file.'.unsort');
	exec(POSTFIX_LOCALBASE."/sbin/postmap ".POSTFIX_LOCALBASE."/etc/postfix/relay_recipients");
	mwexec("/usr/local/sbin/postfix reload");
	}
	if($relay_recipients !="" || $relay_ldap_recipients!="")
		return("relay_recipient_maps = hash:".POSTFIX_LOCALBASE."/etc/postfix/relay_recipients\n");

}
function check_cron(){
	global $config, $g;
			#check crontab
			$new_cron=array();
			$cron_cmd_sqlite = "";
			$cron_postfix_sqlite="";
			$cron_cmd= "/usr/local/bin/php -q /usr/local/www/postfix_recipients.php";
			$postfix_enabled=$config['installedpackages']['postfix']['config'][0]['enable_postfix'];
			#check ldap update
			if (is_array($config['installedpackages']['postfixrecipients']['config']))
				$postfix_recipients_config=$config['installedpackages']['postfixrecipients']['config'][0];
			if(preg_match("/(\d+)(\w)/",$postfix_recipients_config['freq'],$matches)){
					$cron_postfix=array("minute" =>	"*",
										"hour"	 =>	"*",
										"mday" 	 =>	"*",
										"month"  =>	"*",
										"wday"	 =>	"*",
										"who"	 =>	"root",
										"command"=>	$cron_cmd);
					switch ($matches[2]){
						case m:
							$cron_postfix["minute"]="*/".$matches[1];
							break;
						case h:
							$cron_postfix["minute"]="0";
							$cron_postfix["hour"]="*/".$matches[1];
							break;
						case d:
							$cron_postfix["minute"]="0";
							$cron_postfix["hour"]="0";
							$cron_postfix["mday"]="*/".$matches[1];
							break;
						default:
							$input_errors[] = "A valid number with a time reference is required for the field 'Frequency'";
					}
			}
			#check crontab Sqlite databases
			if (is_array($config['installedpackages']['postfix']['config']) && $postfix_enabled=="on"){
				$cron_sqlite_queue=$config['installedpackages']['postfix']['config'][0]['update_sqlite'];
				$cron_cmd_sqlite="/usr/local/bin/php -q /usr/local/www/postfix.php";
				if ($cron_sqlite_queue != "" && $cron_sqlite_queue != "never"){
					$cron_postfix_sqlite=array("minute" =>	"*",
											"hour"	 =>	"*",
											"mday" 	 =>	"*",
											"month"  =>	"*",
											"wday"	 =>	"*",
											"who"	 =>	"root",
											"command"=>	"");
					switch ($cron_sqlite_queue){
						case '01min':
							$cron_postfix_sqlite["command"] = $cron_cmd_sqlite ." 01min";
							break;
						case '10min':
							$cron_postfix_sqlite["minute"]="*/10";
							$cron_postfix_sqlite["command"] = $cron_cmd_sqlite ." 10min";
							break;
						case '01hour':
							$cron_postfix_sqlite["minute"]="0";
							$cron_postfix_sqlite["command"] = $cron_cmd_sqlite ." 01hour";
							break;
						case '24hours':
							$cron_postfix_sqlite["minute"]="0";
							$cron_postfix_sqlite["hour"]="0";
							$cron_postfix_sqlite["command"] = $cron_cmd_sqlite ." 24hours";
							break;
						}
				}
			}

			#check crontab relay recipients
			$cron_found="";
			if (is_array($config['cron']['item'])){
				#print "<pre>";
				foreach($config['cron']['item'] as $cron){
					#check valid_recipients cron
					if ($cron["command"] == $cron_cmd){
						#postfix cron cmd found
						if($postfix_enabled=="on"){
							$cron_found=$cron;
							if($postfix_recipients_config['enable_ldap'] || $postfix_recipients_config['enable_url']){
								#update cron schedule
								$new_cron['item'][]=$cron_postfix;
								}
							}
						}
					#check sqlite update queue
					else if(!preg_match("/.usr.local.www.postfix.php/",$cron["command"])){
						#keep all non postfix cron cmds if not empty
						if ($cron["command"] != "")
							$new_cron['item'][]=$cron;
						}
					}
			$write_cron=1;
			# Check if crontab must be changed to valid recipients cmd
			if ($postfix_recipients_config['enable_ldap'] || $postfix_recipients_config['enable_url']){
				if ($cron_found!=$cron_postfix){
					#update postfix cron schedule
					if (! is_array($cron_found) && $postfix_enabled=="on")
						$new_cron['item'][]=$cron_postfix;
					$write_cron=1;
					}
				}
			else{
				if (is_array($cron_found)){
					#remove postfix cron cmd
					$write_cron=1;
					}
				}
			#check if cron must be changed to Sqlite cmd
			if($cron_sqlite_queue != "" && $cron_sqlite_queue != "never"){
				$new_cron['item'][]=$cron_postfix_sqlite;
				$config['cron']=$new_cron;
				$write_cron=1;
				}
			}

			#call cron functions
			if ($write_cron==1){
				$config['cron']=$new_cron;
				write_config('Postfix - sync remote sqlite database',$backup = false);
				configure_cron();
				}
			#remove postfix old cron call
			$old_cron=0;
			$crontab = file('/etc/crontab');
			$new_crontab="";
			foreach ($crontab as $line){
				if (preg_match("/php..usr.local.www.postfix_recipients.php/",$line))
					$old_cron=1;
				else
					$new_crontab .= $line;
				}
			if ($old_cron==1)
				file_put_contents("/etc/crontab",$new_crontab, LOCK_EX);

			#print "<pre>". var_dump($new_cron). var_dump($cron_postfix_sqlite).var_dump($config['cron']);
			#exit;


}
function sync_package_postfix($via_rpc="no") {
	global $config;

	log_error("sync_package_postfix called with via_rpc={$via_rpc}");
	# detect boot process
	if (is_array($_POST)){
		if (preg_match("/\w+/",$_POST['__csrf_magic']))
			unset($boot_process);
		else
			$boot_process="on";
	}

	if(is_process_running("master") && isset($boot_process) && $via_rpc=="no")
		return;

	#check patch in /etc/inc/config.
	$relay_domains = "";
	$transport = "";
	$postfix_config=$config['installedpackages']['postfix']['config'][0];
	if (is_array($config['installedpackages']['postfixdomains']))
		$postfix_domains=$config['installedpackages']['postfixdomains']['config'][0];
	$message_size_limit=($postfix_config['message_size_limit']?$postfix_config['message_size_limit']:"10240000");
	$process_limit=($postfix_config['process_limit']?$postfix_config['process_limit']:"100");
	if (is_array($postfix_domains['row'])) {
		foreach ($postfix_domains['row'] as $postfix_row) {
			$relay_domains .= ' ' . $postfix_row['domain'];
			if (!empty($postfix_row['mailserverip']))
				$transport .= $postfix_row['domain'] . " smtp:[" . $postfix_row['mailserverip'] . "]\n";
				}
			}
	#check cron
	check_cron();
	#check logging
	if ($postfix_config['log_to']){
			switch($postfix_config['log_to']){
				case 'maillog':
					system("/usr/bin/touch /var/log/maillog");
					$mail_syslog="mail.crit;";
				break;
				case 'none':
					$mail_syslog="mail.crit;";
				break;
				default:
					$mail_syslog='mail.*;';
				break;
			}
            #update /etc/inc/system.inc
            $sys_log_file='/etc/inc/system.inc';
            $pfsense_version=preg_replace("/\s/","",file_get_contents("/etc/version"));
            $sys_log = file($sys_log_file);
            $new_sys_log="";
            $found_mail=0;
            foreach ($sys_log as $line){
            	$new_line=preg_replace('/mail.(.|crit);/',$mail_syslog,$line);
            	if (preg_match('/mail.*system.log/',$line) && $postfix_config['log_to'] =="maillog"){
            		$new_sys_log .= 'mail.*'."\t\t\t\t\t\t".'/var/log/maillog'."\n";
            	}
            	if (preg_match('/maillog/',$line)){
            		$new_line ="";
            	}
            	$new_sys_log .= $new_line;
            }
            if (!file_exists('/root/'.$pfsense_version.'.system.inc.backup')) {
            	copy ($sys_log_file,'/root/'.$pfsense_version.'.system.inc.backup');
            }
            file_put_contents($sys_log_file,$new_sys_log, LOCK_EX);
            #mwexec('/usr/local/bin/php -q /usr/local/www/postfix_syslog.php');
            #restart syslog daemon
            system_syslogd_start();
		}

	#check_debug
	if($postfix_config['debug_list'] && $postfix_config['debug_list']!=""){
	$check_debug ="\n#Debugging postfix\n";
	$check_debug.="debug_peer_list = ".px_text_area_decode($postfix_config['debug_list'])."\n";
	$check_debug.="debug_peer_level = ".$postfix_config['debug_level']."\n\n";
	}
	#check relay recipients
	$all_relay_recipients=sync_relay_recipients('gui');

	$copyright=<<<ABOUT
#Part of the Postfix package for pfSense
#Copyright (C) 2010 Erik Fonnesbeck
#Copyright (C) 2011-2013 Marcello Coutinho
#All rights reserved.
#DO NOT EDIT THIS FILE


ABOUT;
$pf_dir=POSTFIX_LOCALBASE;
	$postfix_main=<<<EOF
#main.cf\
{$copyright}
mynetworks = {$pf_dir}/etc/postfix/mynetwork_table
mynetworks_style = host
access_map_reject_code= 554
access_map_defer_code = 451
unverified_recipient_reject_code = 550
unknown_client_reject_code = 550
unknown_hostname_reject_code = 550

EOF;
	#Header Maps
	$mynetworks = "";
	if ($config['installedpackages']['postfixacl']['config'][0]['mynetworks']){
		$mynetworks = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['mynetworks']);
		}
	#Header Maps
	if ($config['installedpackages']['postfixacl']['config'][0]['header_maps']){
		$postfix_main .= "header_checks = pcre:".POSTFIX_LOCALBASE."/etc/postfix/header_check\n";
		$postfix_main .= "header_size_limit = 1024000\n";
		$header_check = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['header_maps']);
		}
	#Helo Maps
	if ($config['installedpackages']['postfixacl']['config'][0]['helo_maps']){
		$helo_check = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['helo_maps']);
		}
	#Sender access
	if ($config['installedpackages']['postfixacl']['config'][0]['sender_access']){
		$sender_access = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['sender_access']);
		}
	#MIME Maps
	if ($config['installedpackages']['postfixacl']['config'][0]['mime_maps']){
		$postfix_main .= "mime_header_checks = pcre:".POSTFIX_LOCALBASE."/etc/postfix/mime_check\n";
		$mime_check = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['mime_maps']);
		}
	#Body Maps
	if ($config['installedpackages']['postfixacl']['config'][0]['body_maps']){
		$postfix_main .= "body_checks = pcre:".POSTFIX_LOCALBASE."/etc/postfix/body_check\n";
		$body_check = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['body_maps']);
		}
	#Client CIDR
	if ($config['installedpackages']['postfixacl']['config'][0]['cal_cidr']){
		if ($antispam['zombie_blocker']=='disabled')
			$cal_cidr = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['cal_cidr']);
		else
			#formatar o arquivo retirando os 'oks'
			$cal_cidr_tmp = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['cal_cidr']);
			$cal_cidr = preg_replace('/ ok/i'," permit",$cal_cidr_tmp);
		}
	#Client PCRE
	if ($config['installedpackages']['postfixacl']['config'][0]['cal_pcre']){
		$cal_pcre = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['cal_pcre']);
		}
	$postfix_main .= px_text_area_decode($postfix_config['maincf'])."\n".
		"relay_domains ={$relay_domains}\n" .
		"transport_maps = hash:".POSTFIX_LOCALBASE."/etc/postfix/transport\n" .
		"local_recipient_maps =\n" .
		$all_relay_recipients.
		"mydestination =\n" .
		"mynetworks_style = host\n" .
		"message_size_limit = {$message_size_limit}\n" .
		"default_process_limit = {$process_limit}\n";
	#assign antispam options
	$antispam=$config['installedpackages']['postfixantispam']['config'][0];

	if($antispam['antispam_enabled']){
		switch ($antispam['antispam_software']){
			case "mailscanner":
			if ($antispam['hold_mode']=='auto'){
				$header_check .= "\n/^Received:/ HOLD\n";
				$postfix_main_antispam = "#Saving all mail after header/body/rbl/spf checks to mailscanner\n\n";
				}
			break;
			case "policyd2":
			if ($antispam['antispam_location']){
			$postfix_main_antispam = <<<EOF
#using policyd v2
client_throttle = check_policy_service {$antispam['antispam_location']}
smtpd_client_restrictions = check_policy_service {$antispam['antispam_location']}
smtpd_restriction_classes =
		has_our_domain_as_sender
		client_throttle
smtpd_end_of_data_restrictions = check_policy_service {$antispam['antispam_location']}


EOF;
			}
			else{
			$postfix_main_antispam = "Policyd v2 has no location set.\n\n";
			}
			break;
		}
	}
	if ($antispam['reject_unknown_helo_hostname']){
		$reject_unknown_helo_hostname = <<<EOF
smtpd_helo_restrictions = check_helo_access pcre:{$pf_dir}/etc/postfix/helo_check,
				reject_unknown_helo_hostname,
				reject_invalid_helo_hostname,
				reject_non_fqdn_helo_hostname,
				permit

EOF;
		}
	if ($antispam['header_check'] == "strong")
	{
	$postfix_main .= <<<EOF
disable_vrfy_command = yes
strict_rfc821_envelopes = yes

#Just reject after helo,sender,client,recipient tests
smtpd_delay_reject = yes

# Don't talk to mail systems that don't know their own hostname.
smtpd_helo_required = yes
{$reject_unknown_helo_hostname}

smtpd_sender_restrictions = reject_non_fqdn_sender,
				reject_unknown_sender_domain,
				reject_unauth_pipelining,
				reject_multi_recipient_bounce,
				permit

# Allow connections from specified local clients and strong check everybody else.
smtpd_client_restrictions = permit_mynetworks,
				reject_unauth_destination,
				check_client_access pcre:{$pf_dir}/etc/postfix/cal_pcre,
				check_client_access cidr:{$pf_dir}/etc/postfix/cal_cidr,
				reject_unknown_client_hostname,
				reject_unauth_pipelining,
				reject_multi_recipient_bounce,
				permit

smtpd_recipient_restrictions = permit_mynetworks,
				reject_unauth_destination,
				reject_unauth_pipelining,
				check_client_access pcre:{$pf_dir}/etc/postfix/cal_pcre,
				check_client_access cidr:{$pf_dir}/etc/postfix/cal_cidr,
				check_sender_access hash:{$pf_dir}/etc/postfix/sender_access,
				reject_non_fqdn_helo_hostname,
				reject_unknown_recipient_domain,
				reject_non_fqdn_recipient,
				reject_multi_recipient_bounce,
				reject_unverified_recipient,
				SPFSPFSPFRBLRBLRBL

EOF;
	}
else
	{
	$postfix_main .= <<<EOF
#Just reject after helo,sender,client,recipient tests
smtpd_delay_reject = yes

# Don't talk to mail systems that don't know their own hostname.
smtpd_helo_required = yes
{$reject_unknown_helo_hostname}

smtpd_sender_restrictions = reject_unknown_sender_domain,
				RBLRBLRBL

# Allow connections from specified local clients and rbl check everybody else if rbl check are set.
smtpd_client_restrictions = permit_mynetworks,
				reject_unauth_destination,
				check_sender_access hash:{$pf_dir}/etc/postfix/sender_access,
				check_client_access pcre:{$pf_dir}/etc/postfix/cal_pcre,
				check_client_access cidr:{$pf_dir}/etc/postfix/cal_cidr
				RBLRBLRBL

# Whitelisting: local clients may specify any destination domain.
#,
smtpd_recipient_restrictions = permit_mynetworks,
				reject_unauth_destination,
				check_sender_access hash:{$pf_dir}/etc/postfix/sender_access,
				check_client_access pcre:{$pf_dir}/etc/postfix/cal_pcre,
				check_client_access cidr:{$pf_dir}/etc/postfix/cal_cidr,
				SPFSPFSPFRBLRBLRBL

EOF;
	}
#check spf option
switch($antispam['postfix_spf']){
	case 'spf_mark_only':
		$postfix_main.= "spf_mark_only = yes\n";
		$spf="reject_spf_invalid_sender,\n\t\t\t\t";
		break;
	case 'disable':
		$spf="";
		break;
	default:
		$spf=$antispam['postfix_spf'].",\n\t\t\t\t";
		break;
}
$postfix_main=preg_replace("/SPFSPFSPF/",$spf,$postfix_main);
$postfix_main .= $postfix_main_antispam.$check_debug;
switch ($antispam['zombie_blocker'])
		{
		case "enforce":
		case "drop":
		case "ignore":
		$postscreen=1;
		break;

		case "disabled":
		$postscreen=0;
		break;
		}
	if ($antispam['soft_bounce'] == "enabled")
	{
	$postfix_main.="soft_bounce = yes\n";
	}

	//check ips to listen on
	$inet_protocols=($postfix_config['inet_protocol'] ? $postfix_config['inet_protocol'] : "ipv4");
	$inet_interfaces =array();
	if (preg_match("/All/",$postfix_config['enabled_interface'])){
		$inet_interfaces[]="";
		}
	elseif ($postfix_config['enabled_interface'] == "lo0"){
		$inet_interfaces[]="loopback-only";
		}
	else{
		$ifaces = ($postfix_config['enabled_interface'] ? $postfix_config['enabled_interface'] : 'wan');
		foreach (explode(',',$ifaces) as $listenon){
			if (is_ipaddrv6($listenon) && preg_match("/(ipv6|all)/i",$inet_protocols))
				$inet_interfaces[]= "{$listenon}";
			elseif (is_ipaddr($listenon) && preg_match("/(ipv4|all)/i",$inet_protocols))
				$inet_interfaces[]= "{$listenon}";
			else{
				$listenon=(pfSense_get_interface_addresses(convert_friendly_interface_to_real_interface_name($listenon)));
				if (is_ipaddr($listenon['ipaddr']) && preg_match("/(ipv4|all)/i",$inet_protocols))
					$inet_interfaces []= "{$listenon['ipaddr']}";
				if(is_ipaddrv6($listenon['ipaddr6']) && preg_match("/(ipv6|all)/i",$inet_protocols))
					$inet_interfaces []= "{$listenon['ipaddr6']}";
			}
		}
	}
	$postfix_main.= "inet_protocols = {$inet_protocols}\n";
	$postfix_main.= "inet_interfaces = ".implode(",",$inet_interfaces)."\n";

	if ($postscreen==1)	#Postscreen enabled
	{
	if(preg_match("/(\d+),(\d+)(s|m|h|w)/",$antispam['greet_time'],$greet)){
			$postfix_main.='postscreen_greet_wait = ${stress?'.$greet[1].'}${stress:'.$greet[2].'}'.$greet[3]."\n";
			}
		$ag=$antispam['after_greeting'];
		if(preg_match("/postscreen_disable_vrfy_command/",$antispam['after_greeting'])){
			$postfix_main.="postscreen_disable_vrfy_command = yes\n";
			}
		if(preg_match("/postscreen_non_smtp_command_enable/",$antispam['after_greeting'])){
			$postfix_main.="postscreen_non_smtp_command_enable = yes\n";
			$postfix_main.="postscreen_non_smtp_command_action = ".$antispam['zombie_blocker']."\n";
			}
		if(preg_match("/postscreen_pipelining_enable/",$antispam['after_greeting'])){
			$postfix_main.="postscreen_pipelining_enable = yes\n";
			$postfix_main.="postscreen_pipelining_action = ".$antispam['zombie_blocker']."\n";
			}
		if(preg_match("/postscreen_bare_newline_enable/",$antispam['after_greeting'])){
			$postfix_main.="postscreen_bare_newline_enable = yes\n";
			$postfix_main.="postscreen_bare_newline_action = ".$antispam['zombie_blocker']."\n";
			}
		if(preg_match("/postscreen_greet_check/",$antispam['after_greeting'])){
			$postfix_main.="postscreen_greet_action = ".$antispam['zombie_blocker']."\n";
			}

	$postfix_main.="postscreen_access_list = permit_mynetworks,\n\t\t\tcidr:".POSTFIX_LOCALBASE."/etc/postfix/cal_cidr\n";
	$postfix_main.="postscreen_dnsbl_action= ".$antispam['zombie_blocker']."\n";
	$postfix_main.="postscreen_blacklist_action= ".$antispam['zombie_blocker']."\n";

	#postscreen interface loop
	//$ifaces = ($postfix_config['enabled_interface'] ? $postfix_config['enabled_interface'] : 'wan');
	//$real_ifaces = array();
	//$postfix_master="";
	//foreach (explode(",", $ifaces) as $i => $iface) {
	//	$real_ifaces[] = px_get_real_interface_address($iface);
	//	if($real_ifaces[$i][0]) {
	//		$postfix_master .=$real_ifaces[$i][0].":25	inet  n       -       n       -       1       postscreen\n\t-o user=postfix\n";
	$postfix_master = "smtp	inet  n       -       n       -       1       postscreen\n\t-o user=postfix\n";
	$postfix_master .=($antispam['soft_bounce'] == "postscreen"?"\t-o soft_bounce=yes\n":"");
	//	}
	//}
	$postfix_master .= $postfix_inets.<<<MASTEREOF
smtpd     pass  -       -       n       -       -       smtpd
dnsblog   unix  -       -       n       -       0       dnsblog
tlsproxy  unix  -       -       n       -       0       tlsproxy

MASTEREOF;
	$rbl2="";
	if ($antispam['rbl_servers'] != "")
		{
		$postfix_main .= "postscreen_dnsbl_sites=" . $antispam['rbl_servers']."\n";
		$postfix_main .= "postscreen_dnsbl_threshold=" . $antispam['rbl_threshold']."\n";
		}
	}
	else
	{					#Postscreen disabled
	if ($antispam['rbl_servers'] != "")
		{
		$RBL = explode(",",$antispam['rbl_servers']);
		foreach ($RBL as $rbl)
			{
			$prefix=($rbl2 !=""?"\t\t\t\t":"");
			$rbl2.= $prefix."reject_rbl_client $rbl,\n";
			}
		}

	#interface loop
	/*$postfix_inets="";
	$ifaces = ($postfix_config['enabled_interface'] ? $postfix_config['enabled_interface'] : 'loopback');
	$real_ifaces = array();
	$postfix_master="";
	foreach (explode(",", $ifaces) as $i => $iface) {
		$real_ifaces[] = px_get_real_interface_address($iface);
		if($real_ifaces[$i][0]) {
			$postfix_master .=$real_ifaces[$i][0].":25	inet  n       -       n       -       -       smtpd\n";
		}
	}
	*/
	$postfix_master ="25	inet  n       -       n       -       -       smtpd\n";

	}
	$rbl2.=($rbl2 !=""?"\t\t\t\tpermit\n":"permit\n");
	$postfix_main=preg_replace("/RBLRBLRBL/",$rbl2,$postfix_main);

	#Header Maps
	$anvil_config=$config['installedpackages']['postfixantispam']['config'][0]['anvil'];
	if ($anvil_config =='enabled' || ($anvil_config =='postscreen' && $postscreen==1))
		$anvil='anvil     unix  -       -       n       -       1       anvil';

	$postfix_master .= <<<MASTEREOF2
pickup    fifo  n       -       n       60      1       pickup
cleanup   unix  n       -       n       -       0       cleanup
qmgr      fifo  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       n       1000?   1       tlsmgr
rewrite   unix  -       -       n       -       -       trivial-rewrite
bounce    unix  -       -       n       -       0       bounce
defer     unix  -       -       n       -       0       bounce
trace     unix  -       -       n       -       0       bounce
verify    unix  -       -       n       -       1       verify
flush     unix  n       -       n       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       n       -       -       smtp
relay     unix  -       -       n       -       -       smtp
	-o smtp_fallback_relay=
showq     unix  n       -       n       -       -       showq
error     unix  -       -       n       -       -       error
retry     unix  -       -       n       -       -       error
discard   unix  -       -       n       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp
scache    unix  -       -       n       -       1       scache
{$anvil}

MASTEREOF2;

	conf_mount_rw();

	//check postfix etc dir on 2.2
	$pfs_version = substr(trim(file_get_contents("/etc/version")),0,3);
	$postfix_etc_lnk="/usr/local/etc/postfix";
	if ($pfs_version == 2.2 && !is_dir($postfix_etc_lnk))
		@symlink(POSTFIX_LOCALBASE.'/etc/postfix',$postfix_etc_lnk);

	log_error("Writing out configuration");
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/main.cf", $postfix_main, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/master.cf", $postfix_master, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/transport", $transport, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/sender_access", $sender_access, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/cal_cidr", $cal_cidr, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/cal_pcre", $cal_pcre, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/header_check", $header_check, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/helo_check", $helo_check, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/mime_check", $mime_check, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/body_check", $body_check, LOCK_EX);
	file_put_contents(POSTFIX_LOCALBASE."/etc/postfix/mynetwork_table", $mynetworks, LOCK_EX);
	$FILES=array("transport","sender_access");
	foreach ($FILES as $file)
		{
		mwexec(POSTFIX_LOCALBASE."/sbin/postmap ".POSTFIX_LOCALBASE."/etc/postfix/".$file);
		}

	#check postix dirs
	$dirs=array("/var/spool/postfix","/etc/mail","/var/db/postfix","/var/mail/postfix");
	foreach ($dirs as $dir)
		if (!is_dir($dir) && !file_exists($dir))
			mkdir($dir, 0755,TRUE);

	#check postfix owners
	$dirs=array("/var/db/postfix","/var/mail/postfix");
	foreach ($dirs as $dir){
			chown($dir, 'postfix');
			chgrp($dir, 'postfix');
			}
	if (!file_exists("/etc/mail/aliases"))
		touch("/etc/mail/aliases");
	exec("/usr/local/bin/newaliases");
	postfix_start();

	#Do not sync during boot
	if(!isset($boot_process) || $via_rpc=="yes")
		postfix_sync_on_changes();

}
function postfix_start(){
	global $config;
	$pf_dir=POSTFIX_LOCALBASE;
	$start=<<<EOF

	sysctl kern.ipc.nmbclusters=65536
	sysctl kern.ipc.somaxconn=16384
	sysctl kern.maxfiles=131072
	sysctl kern.maxfilesperproc=104856
	sysctl kern.threads.max_threads_per_proc=4096
	{$pf_dir}/sbin/postfix start

EOF;
	$stop = POSTFIX_LOCALBASE."/sbin/postfix stop\n";
	log_error("Writing rc_file");
	write_rcfile(array("file" => "postfix.sh", "start" => $start, "stop" => $stop));

	sleep(1);
	if (is_array($config['installedpackages']['postfix']) && $config['installedpackages']['postfix']['config'][0]['enable_postfix']){
		log_error("Reloading/starting postfix");
		system('/bin/chmod +x /usr/local/etc/rc.d/postfix.sh');
		mwexec_bg(POSTFIX_LOCALBASE."/sbin/postfix reload || /usr/local/etc/rc.d/postfix.sh start");
		log_error("Postfix setup completed");
		}
	else{
		log_error("Stopping postfix");
		mwexec("/usr/local/etc/rc.d/postfix.sh stop");
		system('/bin/chmod -x /usr/local/etc/rc.d/postfix.sh');
		}

	conf_mount_ro();
}

function postfix_validate_input($post, &$input_errors) {
	foreach ($post as $key => $value) {
		if (empty($value))
			continue;
		if($key == "greet_time" && !preg_match("/(\d+),(\d+)(s|m|h|w)/",$value))
				$input_errors[] = "Wrong greet time sintax.";
		if($key == "message_size_limit" && !is_numeric($value))
				$input_errors[] = "Message size limit must be numeric.";
		if($key == "process_limit" && !is_numeric($value))
				$input_errors[] = "Process limit must be numeric.";
		if($key == "freq" && (!preg_match("/^\d+(h|m|d)$/",$value) || $value == 0))
				$input_errors[] = "A valid number with a time reference is required for the field 'Frequency'";
		if (substr($key, 0, 2) == "dc" && !is_hostname($value))
				$input_errors[] = "{$value} is not a valid host name.";
		if (substr($key, 0, 6) == "domain" && is_numeric(substr($key, 6))) {
			if (!is_domain($value))
				$input_errors[] = "{$value} is not a valid domain name.";
		} else if (substr($key, 0, 12) == "mailserverip" && is_numeric(substr($key, 12))) {
			if (empty($post['domain' . substr($key, 12)]))
				$input_errors[] = "Domain for {$value} cannot be blank.";
			if (!is_ipaddr($value) && !is_hostname($value))
				$input_errors[] = "{$value} is not a valid IP address or host name.";
		}
	}
}

function postfix_php_install_command() {
	sync_package_postfix();
}

function postfix_php_deinstall_command() {
	global $config;
	#disable service
	if (is_array($config['installedpackages']['postfix']))
		$config['installedpackages']['postfix']['config'][0]['enable_postfix']="";
	write_config();
	sync_package_postfix();
	conf_mount_rw();
	unlink_if_exists("/usr/local/etc/rc.d/postfix.sh");
	conf_mount_ro();
}

/* Uses XMLRPC to synchronize the changes to a remote node */
function postfix_sync_on_changes() {
	global $config, $g;
	if (is_array($config['installedpackages']['postfixsync']['config'])){
		$postfix_sync=$config['installedpackages']['postfixsync']['config'][0];
		$synctimeout = $postfix_sync['synctimeout'];
		$synconchanges = $postfix_sync['synconchanges'];
		switch ($synconchanges){
			case "manual":
				if (is_array($postfix_sync[row])){
					$rs=$postfix_sync[row];
					}
				else{
					log_error("[postfix] xmlrpc sync is enabled but there is no hosts to push postfix config.");
					return;
					}
				break;
			case "auto":
					if (is_array($config['installedpackages']['carpsettings']) && is_array($config['installedpackages']['carpsettings']['config'])){
						$system_carp=$config['installedpackages']['carpsettings']['config'][0];
						$rs[0]['ipaddress']=$system_carp['synchronizetoip'];
						$rs[0]['username']=$system_carp['username'];
						$rs[0]['password']=$system_carp['password'];
						$rs[0]['enabless']=true;
						$rs[0]['sync_type']="xmlrpc";
						if (! is_ipaddr($system_carp['synchronizetoip'])){
							log_error("[postfix] xmlrpc sync is enabled but there is no system backup hosts to push postfix config.");
							return;
							}
					}
					else{
						log_error("[postfix] xmlrpc sync is enabled but there is no system backup hosts to push postfix config.");
						return;
					}
				break;
			default:
				return;
			break;
		}
		if (is_array($rs)){
			log_error("[postfix] xmlrpc sync is starting.");
			foreach($rs as $sh){
				$sync_to_ip = $sh['ipaddress'];
				if($sh['username'])
					$username = $sh['username'];
				else
					$username = 'admin';
				if($sh['password'] && $sh['ipaddress'] && $sh['enabless'])
					postfix_do_xmlrpc_sync($sh['ipaddress'], $username, $sh['password'],$sh['sync_type'],$synctimeout);
				}
			log_error("[postfix] xmlrpc sync is ending.");
			}
 		}
}


/* Do the actual XMLRPC sync */
function postfix_do_xmlrpc_sync($sync_to_ip,$username,$password,$sync_type,$synctimeout) {
	global $config, $g;

	if(!$username)
		$username="admin";

	if(!$password)
		return;

	if(!$sync_to_ip)
		return;

	if(!$synctimeout)
		$synctimeout=120;

	$xmlrpc_sync_neighbor = $sync_to_ip;
    if($config['system']['webgui']['protocol'] != "") {
		$synchronizetoip = $config['system']['webgui']['protocol'];
		$synchronizetoip .= "://";
    }
    $port = $config['system']['webgui']['port'];
    /* if port is empty lets rely on the protocol selection */
    if($port == "") {
		if($config['system']['webgui']['protocol'] == "http")
			$port = "80";
		else
			$port = "443";
    }
	$synchronizetoip .= $sync_to_ip;

	/* xml will hold the sections to sync */
	$xml = array();
	$sync_xml=$config['installedpackages']['postfixsync']['config'][0]['synconchanges'];
	$sync_db=$config['installedpackages']['postfixsync']['config'][0]['rsync'];
	if ($sync_xml && preg_match("/xmlrpc/",$sync_type)){
		log_error("Include postfix xmls");
		$xml['postfix'] = $config['installedpackages']['postfix'];
		$xml['postfixdomains'] = $config['installedpackages']['postfixdomains'];
		$xml['postfixacl'] = $config['installedpackages']['postfixacl'];
		$xml['postfixrecipients'] = $config['installedpackages']['postfixrecipients'];
		$xml['postfixantispam'] = $config['installedpackages']['postfixantispam'];
		}
	if (count($xml) > 0){
		/* assemble xmlrpc payload */
		$params = array(
			XML_RPC_encode($password),
			XML_RPC_encode($xml)
		);

		/* set a few variables needed for sync code borrowed from filter.inc */
		$url = $synchronizetoip;
		log_error("Beginning Postfix XMLRPC sync to {$url}:{$port}.");
		$method = 'pfsense.merge_installedpackages_section_xmlrpc';
		$msg = new XML_RPC_Message($method, $params);
		$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
		$cli->setCredentials($username, $password);
		if($g['debug'])
			$cli->setDebug(1);
		/* send our XMLRPC message and timeout after $sync_timeout seconds */
		$resp = $cli->send($msg, $synctimeout);
		if(!$resp) {
			$error = "A communications error occurred while attempting postfix XMLRPC sync with {$url}:{$port}.";
			log_error($error);
			file_notice("sync_settings", $error, "Postfix Settings Sync", "");
		} elseif($resp->faultCode()) {
			$cli->setDebug(1);
			$resp = $cli->send($msg, $synctimeout);
			$error = "An error code was received while attempting postfix XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
			log_error($error);
			file_notice("sync_settings", $error, "Postfix Settings Sync", "");
		} else {
			log_error("Postfix XMLRPC sync successfully completed with {$url}:{$port}.");
		}

		/* tell postfix to reload our settings on the destionation sync host. */
		$method = 'pfsense.exec_php';
		$execcmd  = "require_once('/usr/local/pkg/postfix.inc');\n";
		$execcmd .= "sync_package_postfix('yes');";

		/* assemble xmlrpc payload */
		$params = array(
			XML_RPC_encode($password),
			XML_RPC_encode($execcmd)
		);

		log_error("postfix XMLRPC reload data {$url}:{$port}.");
		$msg = new XML_RPC_Message($method, $params);
		$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
		$cli->setCredentials($username, $password);
		$resp = $cli->send($msg, $synctimeout);
		if(!$resp) {
			$error = "A communications error occurred while attempting postfix XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
			log_error($error);
			file_notice("sync_settings", $error, "postfix Settings Sync", "");
		} elseif($resp->faultCode()) {
			$cli->setDebug(1);
			$resp = $cli->send($msg, $synctimeout);
			$error = "An error code was received while attempting postfix XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
			log_error($error);
			file_notice("sync_settings", $error, "postfix Settings Sync", "");
		} else {
			log_error("postfix XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
		}
	}
}

?>