<?php
/*
	postfix.inc
	part of the Postfix package for pfSense
	Copyright (C) 2010 Erik Fonnesbeck
	Copyright (C) 2011 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.

*/
require_once("util.inc");
require_once("functions.inc");
require_once("pkg-utils.inc");
require_once("globals.inc");

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;
	#relay recipients
	if ($config['installedpackages']['postfixrecipients']['config']) {
		$relay_recipients="";
		$relay_ldap_recipients="";
		$ad_export="/usr/local/etc/postfix/adexport.pl";
		$cron_cmd="/usr/local/bin/php -q /usr/local/www/postfix_recipients.php";
		$postfix_enabled=$config['installedpackages']['postfix']['config'][0]['enable_postfix'];
		foreach ($config['installedpackages']['postfixrecipients']['config'] as $postfix_recipients_config) {
			if($postfix_recipients_config['location'] && file_exists($postfix_recipients_config['location']))
				$relay_recipients .= file_get_contents($postfix_recipients_config['location']);
			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(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["month"]="*/".$matches[1];
							break;
						case h:
							$cron_postfix["hour"]="*/".$matches[1];
							break;
						case d:
							$cron_postfix["mday"]="*/".$matches[1];
							break;
						default:
							$input_errors[] = "A valid number with a time reference is required for the field 'Frequency'";
					}
				$relay_ldap_recipients="";
				if ($via_cron == "gui"){
					#running via pfsense gui, not time for ldap fetch.
					$ldap_recipients='/usr/local/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="/usr/local/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 != ""?$recipient." OK\n":"");
						
					#save ldap relay recipients
					file_put_contents("/usr/local/etc/postfix/relay_ldap_recipients.txt",$relay_ldap_recipients, LOCK_EX);
					}
				}
			}
			#check crontab 
			$new_cron=array();
			$cron_cmd_sqlite = "";
			$cron_postfix_sqlite="";
			#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 '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_enabled=="on")
							#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']){
				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();
				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);
		}
		
	#save all relay recipients and reload postfix
	file_put_contents("/usr/local/etc/postfix/relay_recipients",$relay_ldap_recipients."\n".$relay_recipients, LOCK_EX);
	exec("/usr/local/sbin/postmap /usr/local/etc/postfix/relay_recipients");
	mwexec("/usr/local/sbin/postfix reload");
	}
	if($relay_recipients !="" || $relay_ldap_recipients!="")
		return("relay_recipient_maps = hash:/usr/local/etc/postfix/relay_recipients\n");
}
function sync_package_postfix() {
	global $config;
	$relay_domains = "";
	$transport = "";
	$postfix_config=$config['installedpackages']['postfix']['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_config['row'])) {
		foreach ($postfix_config['row'] as $postfix_row) {
			$relay_domains .= ' ' . $postfix_row['domain'];
			if (!empty($postfix_row['mailserverip']))
				$transport .= $postfix_row['domain'] . " smtp:[" . $postfix_row['mailserverip'] . "]\n";
				}
			}
	#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';
            $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);
            	#set syslog entry mail.* %/var/log/maillog when log_to = system
            	if (preg_match ('/mail.(.|crit);/',$line) && $postfix_config['log_to'] =="maillog")
            		$new_sys_log .= 'mail.*'."\t\t\t\t\t\t".'/var/log/maillog'."\n";
            	#remove syslog entry mail.* %/var/log/maillog when log_to != system
            	if (preg_match ("/^mail/",$line))
            		$new_sys_log .="";
            	else	
            		$new_sys_log .= $new_line;
            }
            if (!file_exists('/root/system.inc.backup')) {
            	copy ($sys_log_file,'/root/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 Marcello Coutinho
#All rights reserved.
#DO NOT EDIT THIS FILE


ABOUT;
	$postfix_main="#main.cf\n".$copyright;
	#Header Maps
	if ($config['installedpackages']['postfixacl']['config'][0]['header_maps']){
		$postfix_main .= "header_checks = pcre:/usr/local/etc/postfix/header_check\n";
		$header_check = px_text_area_decode($config['installedpackages']['postfixacl']['config'][0]['header_maps']);
		}
	#MIME Maps
	if ($config['installedpackages']['postfixacl']['config'][0]['mime_maps']){
		$postfix_main .= "mime_header_checks = pcre:/usr/local/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:/usr/local/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:/usr/local/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":
			$header_check .= (!preg_match('@/ HOLD@',$header_check)?"\n/^Received:/ HOLD\n":"\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;
		}	
	}
	$reject_unknown_helo_hostname=($antispam['reject_unknown_helo_hostname']?"reject_unknown_helo_hostname":"");
	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
smtpd_helo_restrictions ={$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 = check_client_access pcre:/usr/local/etc/postfix/cal_pcre,
				check_client_access cidr:/usr/local/etc/postfix/cal_cidr,
				reject_unknown_client_hostname,
				reject_unauth_pipelining,
				reject_multi_recipient_bounce,
				permit							

smtpd_recipient_restrictions = reject_invalid_helo_hostname,
				reject_unknown_recipient_domain,
				reject_non_fqdn_helo_hostname,
				reject_non_fqdn_recipient,
				reject_unauth_destination,
				reject_unauth_pipelining,
				reject_multi_recipient_bounce,
				SPFSPFSPFRBLRBLRBL

EOF;
	}
else
	{
	#erro nas listas de bloqueio
	$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
smtpd_helo_restrictions = {$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 = check_client_access pcre:/usr/local/etc/postfix/cal_pcre,
				check_client_access cidr:/usr/local/etc/postfix/cal_cidr,
				RBLRBLRBL

# Whitelisting: local clients may specify any destination domain.
smtpd_recipient_restrictions = reject_unauth_destination,
				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";	
	}
	
	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 = cidr:/usr/local/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 .=($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       -       1       smtpd\n\t-o user=postfix\n";
		}
	}
		
	}
	$rbl2.=($rbl2 !=""?"\t\t\t\tpermit\n":"permit\n");
	$postfix_main=preg_replace("/RBLRBLRBL/",$rbl2,$postfix_main);
	$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
anvil     unix  -       -       n       -       1       anvil
scache    unix  -       -       n       -       1       scache

MASTEREOF2;
	
	conf_mount_rw();

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

	if (!is_dir("/etc/mail"))
		mkdir("/etc/mail", 0755);
	if (!file_exists("/etc/mail/aliases"))
		touch("/etc/mail/aliases");
	exec("/usr/local/bin/newaliases");
	postfix_start();
	postfix_sync_on_changes();
}
function postfix_start(){
	global $config;
	$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
	/usr/local/sbin/postfix start
	
EOF;
	$stop = "/usr/local/sbin/postfix stop\n";
	log_error("Writing rc_file");		
	write_rcfile(array("file" => "postfix.sh", "start" => $start, "stop" => $stop));

	conf_mount_ro();

	sleep(1);
	if ($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("/usr/local/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');
		}
}

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() {
	mwexec("/usr/local/etc/rc.d/postfix.sh stop");
	sleep(1);
	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;
	log_error("[postfix] postfix_xmlrpc_sync.php is starting.");
	$synconchanges = $config['installedpackages']['postfixsync']['config'][0]['synconchanges'];	
	if(!$synconchanges) 
		return;
	foreach ($config['installedpackages']['postfixsync']['config'] as $rs ){
		foreach($rs['row'] as $sh){
		$sync_to_ip = $sh['ipaddress'];
		$password   = $sh['password'];
		if($password && $sync_to_ip)
			postfix_do_xmlrpc_sync($sync_to_ip, $password);
		}
	}
	log_error("[postfix] postfix_xmlrpc_sync.php is ending.");
}

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

	if(!$password)
		return;

	if(!$sync_to_ip)
		return;

	$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();
	$xml['postfix'] = $config['installedpackages']['postfix'];
	$xml['postfixacl'] = $config['installedpackages']['postfixacl'];
	$xml['postfixrecipients'] = $config['installedpackages']['postfixrecipients'];
	$xml['postfixantispam'] = $config['installedpackages']['postfixantispam'];
	
	/* 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('admin', $password);
	if($g['debug'])
		$cli->setDebug(1);
	/* send our XMLRPC message and timeout after 250 seconds */
	$resp = $cli->send($msg, "250");
	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, "250");
		$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();";
	
	/* 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('admin', $password);
	$resp = $cli->send($msg, "250");
	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, "250");
		$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).");
	}

}

?>