<?php
/*
	pfblocker.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");
require_once("filter.inc");
require_once("services.inc");

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

function cb_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"));
	list($dummy, $ip, $dummy2, $netmask) = explode(" ", $line);
	return array($ip, long2ip(hexdec($netmask)));
}

function pfblocker_Range2CIDR($ip_min, $ip_max) {
	#function called without any args
	if ($ip_min == "" || $ip_max == "")
		return "";
	#function called with same ip in min and max
	if ($ip_min == $ip_max)
		return $ip_min. "/32";
	#convert ip to decimal numbers
	$ip_min_long=ip2long($ip_min);
	$ip_max_long=ip2long($ip_max);
	#check long results
	if ($ip_min_long == -1 || $ip_max_long  == -1)
		return "";
	#identify bits mask
	$bits=(32 -strlen(decbin($ip_max_long - $ip_min_long)));
	if ($bits < 0)
		return "";
	#identify first ip on range network
	$network=long2ip(bindec(substr(decbin($ip_min_long),0,$bits).preg_replace("/\d/","0",substr(decbin($ip_min_long),0,(32-$bits)))));
	#print decbin($ip_min_long)."\n".$network."\n";
	return $network . "/". (32 -strlen(decbin($ip_max_long - $ip_min_long)));
	}

function sync_package_pfblocker() {
	global $g,$config;
	if ($g['booting'] == true){
		print "no action during boot process...\n";
	}
	else{
	conf_mount_rw();
	#apply fetch timeout to pfsense-utils.inc
	$pfsense_utils=file_get_contents('/etc/inc/pfsense-utils.inc');
	$new_pfsense_utils=preg_replace("/\/usr\/bin\/fetch -q/","/usr/bin/fetch -T 5 -q",$pfsense_utils);
	if ($new_pfsense_utils != $pfsense_utils)
			file_put_contents('/etc/inc/pfsense-utils.inc',$new_pfsense_utils, LOCK_EX);
			
	$pfblocker_enable=$config['installedpackages']['pfblocker']['config'][0]['enable_cb'];
	$pfblocker_config=$config['installedpackages']['pfblocker']['config'][0];
	$table_limit =($config['system']['maximumtableentries']!= ""?$config['system']['maximumtableentries']:"100000");
	#get local web gui configuration
    $web_local=($config['system']['webgui']['protocol'] != ""?$config['system']['webgui']['protocol']:"http");
    $port = $config['system']['webgui']['port'];
    if($port == "") {
		if($config['system']['webgui']['protocol'] == "http")
			$port = "80";
		else
			$port = "443";
    }
    $web_local .= "://127.0.0.1:".$port.'/pfblocker.php';
	
	#check folders
	$pfbdir='/usr/local/pkg/pfblocker';
	$pfb_alias_dir='/usr/local/pkg/pfblocker_aliases';
	$pfsense_alias_dir='/var/db/aliastables/';
	if (!is_dir($pfbdir))
		mkdir ($pfbdir,0755);
	if (!is_dir($pfb_alias_dir))
		mkdir ($pfb_alias_dir,0755);
	if (! is_dir($pfsense_alias_dir))
		mkdir ($pfsense_alias_dir,0755);
    
	$continents= array(	"Africa" => "pfBlockerAfrica",
						"Antartica" => "pfBlockerAntartica",
						"Asia" => "pfBlockerAsia",
						"Europe" => "pfBlockerEurope",
						"North America" => "pfBlockerNorthAmerica",
						"Oceania" => "pfBlockerOceania",
						"South America" => "pfBlockerSouthAmerica",
						"Top Spammers" => "pfBlockerTopSpammers");
	
	#create rules vars and arrays
	$new_aliases=array();
	$new_aliases_list=array();
	$permit_inbound=array();
	$permit_outbound=array();
	$deny_inbound=array();
	$deny_outbound=array();
	$aliases_list=array();
			#check if pfblocker is enabled or not.
	$deny_action_inbound=($pfblocker_config['inbound_deny_action']!= ""?$pfblocker_config['inbound_deny_action']:"block");
	$deny_action_outbound=($pfblocker_config['outbound_deny_action']!= ""?$pfblocker_config['outbound_deny_action']:"reject");
	$base_rule= array(	"id" => "",
						"tag"=>	"",
						"tagged"=> "",
						"max"=>	 "",
						"max-src-nodes"=>"",
						"max-src-conn"=> "",
						"max-src-states"=>"",
						"statetimeout"=>"",
						"statetype"=>"keep state",
						"os"=> "");
#############################################
#          Assign Countries 				#
#############################################
	foreach ($continents as $continent => $pfb_alias){
	  ${$continent}="";
	  if (is_array($config['installedpackages']['pfblocker'.strtolower(preg_replace('/ /','',$continent))]['config'])){
		$continent_config=$config['installedpackages']['pfblocker'.strtolower(preg_replace('/ /','',$continent))]['config'][0];
		if ($continent_config['action'] != 'Disabled' && $continent_config['action'] != '' && $pfblocker_enable == "on"){
			foreach (explode(",", $continent_config['countries']) as $iso){
				#var_dump ($iso);
				if ($iso <> "" && file_exists($pfbdir.'/'.$iso.'.txt'))
						${$continent} .= file_get_contents($pfbdir.'/'.$iso.'.txt');
		 		}
		if($continent_config['countries'] != "" && $pfblocker_enable == "on"){
			#write alias files
			file_put_contents($pfb_alias_dir.'/'.$pfb_alias.'.txt',${$continent},LOCK_EX);
			file_put_contents($pfsense_alias_dir.'/'.$pfb_alias.'.txt',${$continent}, LOCK_EX);
			#Create alias config
			$new_aliases_list[]=$pfb_alias;
			$new_aliases[]=array("name"=> $pfb_alias,
					  		 	 "url"=> $web_local.'?pfb='.$pfb_alias,
					  		 	 "updatefreq"=> "32",
					  		 	 "address"=>"",
					  		 	 "descr"=> "pfBlocker country list",
					  		 	 "type"=> "urltable",
					  		 	 "detail"=> "DO NOT EDIT THIS ALIAS");
			#Create rule if action permits
			  switch($continent_config['action']){
			  	case "Deny_Both":
			  			$rule = $base_rule;
						$rule["type"] = $deny_action_inbound;
						$rule["descr"]= "$pfb_alias auto rule";
						$rule["source"]= array("address"=> $pfb_alias);
						$rule["destination"]=array("any"=>"");
						if ($pfblocker_config['enable_log'])
								$rule["log"]="";
						$deny_inbound[]=$rule;					
				case "Deny_Outbound":
						$rule = $base_rule;
						$rule["type"] = $deny_action_outbound;
						$rule["descr"]= "$pfb_alias auto rule";
						$rule["source"]=array("any"=>"");
						$rule["destination"]= array("address"=> $pfb_alias);
						if ($pfblocker_config['enable_log'])
								$rule["log"]="";
						$deny_outbound[]=$rule;					
						break;
				case "Deny_Inbound":
						$rule = $base_rule;
						$rule["type"] = $deny_action_inbound;
						$rule["descr"]= "$pfb_alias auto rule";
						$rule["source"]= array("address"=> $pfb_alias);
						$rule["destination"]=array("any"=>"");
						if ($pfblocker_config['enable_log'])
								$rule["log"]="";
						$deny_inbound[]=$rule;					
						break;
				case "Permit_Outbound":
						$rule = $base_rule;
						$rule["type"] = "pass";
						$rule["descr"]= "$pfb_alias auto rule";
						$rule["source"]=array("any"=>"");
						$rule["destination"]= array("address"=> $pfb_alias);
						if ($pfblocker_config['enable_log'])
								$rule["log"]="";
						$permit_outbound[]=$rule;					
						break;
				case "Permit_Inbound":
						$rule = $base_rule;
						$rule["type"] = "pass";
						$rule["descr"]= "$pfb_alias auto rule";
						$rule["source"]= array("address"=> $pfb_alias);
						$rule["destination"]=array("any"=>"");
						if ($pfblocker_config['enable_log'])
								$rule["log"]="";
						$permit_inbound[]=$rule;					
						break;
				}
			
		}
	  }
	  else{
	  	#unlink continent list if any
	  	unlink_if_exists($pfb_alias_dir.'/'.$pfb_alias.'.txt');
	  }
	  
	}
	#mark pfctl aliastable for cleanup
	if (!in_array($pfb_alias, $aliases_list)) 
				$aliases_list[]=$pfb_alias;
	}
	
	#############################################
	#          Assign lists 					#
	#############################################
	#print "<pre>";
	if($config['installedpackages']['pfblockerlists']['config'] != "")
		foreach($config['installedpackages']['pfblockerlists']['config'] as $list){
		 $alias="pfBlocker".preg_replace("/\W/","",$list['aliasname']);
		 #print $list['aliasname'].$list['action']." ".$alias." ".$row['url']."<br>";
			if ($alias != "pfBlocker" && $list['action'] != "" && $list['action'] != 'Disabled' && $pfblocker_enable == "on"){
			  #remove empty lists files if any
			  if (is_array($list['row']))
			  foreach ($list['row'] as $row){
			  	#print $list['aliasname'].$list['action'].$list['cron']." ".$alias." ".$row['url']."$update_local<br>";
				if ($row['url'] != ""){
					$md5_url = md5($row['url']);
					if (file_exists($pfbdir."/".$md5_url.".txt")){ 
						${$alias}.= file_get_contents($pfbdir.'/'.$md5_url.'.txt');
						}
					else{
						if ($row['format'] == "gz")
							$url_list= gzfile($row['url']);
						else
							$url_list= file($row['url']);
						#extract range lists
						$new_file="";
						if (is_array($url_list))
						  foreach ($url_list as $line){
							# CIDR format 192.168.0.0/16
							if (preg_match("/(\d+\.\d+\.\d+\.\d+\/\d+)/",$line,$matches)){
							${$alias}.= $matches[1]."\n";
							$new_file.= $matches[1]."\n";
							}
							# Single ip addresses 
						    if (preg_match("/(\d+\.\d+\.\d+\.\d+)\s+/",$line,$matches)){
							${$alias}.= $matches[1]."/32\n";
							$new_file.= $matches[1]."/32\n";
							}
							# Network range 192.168.0.0-192.168.0.254
							if (preg_match("/(\d+\.\d+\.\d+\.\d+)-(\d+\.\d+\.\d+\.\d+)/",$line,$matches)){
								$cidr= pfblocker_Range2CIDR($matches[1],$matches[2]);
								if ($cidr != ""){
									${$alias}.= $cidr."\n";
									$new_file.= $cidr."\n";
								}
							}
						}
						if ($new_file != "")
							file_put_contents($pfbdir.'/'.$md5_url.'.txt',$new_file, LOCK_EX);
						}
					}
				}
				#check custom network list
				if (pfb_text_area_decode($list['custom']) != "")
					${$alias}.=pfb_text_area_decode($list['custom'])."\n";
				#save alias file if not empty
				if (${$alias} == ""){
					unlink_if_exists($pfb_alias_dir.'/'.$alias.'.txt');
					}
				else{
					file_put_contents($pfb_alias_dir.'/'.$alias.'.txt',${$alias}, LOCK_EX);
					file_put_contents($pfsense_alias_dir.'/'.$alias.'.txt',${$alias}, LOCK_EX);
					#create alias
					$new_aliases_list[]=$alias;
					$new_aliases[]=array("name"=> $alias,
					  		 	 "url"=> $web_local.'?pfb='.$alias,
					  		 	 "updatefreq"=> "32",
					  		 	 "address"=>"",
					  		 	 "descr"=> "pfBlocker user list",
					  		 	 "type"=> "urltable",
					  		 	 "detail"=> "DO NOT EDIT THIS ALIAS");
					#Create rule if action permits
					switch($list['action']){
						case "Deny_Both":
							$rule = $base_rule;
							$rule["type"] = $deny_action_inbound;
							$rule["descr"]= "$alias auto rule";
							$rule["source"]= array("address"=> $alias);
							$rule["destination"]=array("any"=>"");
							if ($pfblocker_config['enable_log'])
								$rule["log"]="";
							$deny_inbound[]=$rule;
						case "Deny_Outbound":
							$rule = $base_rule;
							$rule["type"] = $deny_action_outbound;
							$rule["descr"]= "$alias auto rule";
							$rule["source"]=array("any"=>"");
							$rule["destination"]= array("address"=> $alias);
							if ($pfblocker_config['enable_log'])
								$rule["log"]="";
							$deny_outbound[]=$rule;					
							break;
						case "Deny_Inbound":
							$rule = $base_rule;
							$rule["type"] = $deny_action_inbound;
							$rule["descr"]= "$alias auto rule";
							$rule["source"]= array("address"=> $alias);
							$rule["destination"]=array("any"=>"");
							if ($pfblocker_config['enable_log'])
								$rule["log"]="";
							$deny_inbound[]=$rule;					
							break;
						case "Permit_Outbound":
							$rule = $base_rule;
							$rule["type"] = "pass";
							$rule["descr"]= "$alias auto rule";
							$rule["source"]=array("any"=>"");
							$rule["destination"]= array("address"=> $alias);
							if ($pfblocker_config['enable_log'])
								$rule["log"]="";
							$permit_outbound[]=$rule;					
							break;
						case "Permit_Inbound":
							$rule = $base_rule;
							$rule["type"] = "pass";
							$rule["descr"]= "$alias auto rule";
							$rule["source"]= array("address"=> $alias);
							$rule["destination"]=array("any"=>"");
							if ($pfblocker_config['enable_log'])
									$rule["log"]="";
							$permit_inbound[]=$rule;					
							break;
						}
				}
			#mark pfctl aliastable for cleanup
			if (!in_array($alias, $aliases_list)) 
				$aliases_list[]=$alias;
			}
		else{
			#unlink previous pfblocker alias list if any
			unlink_if_exists($pfb_alias_dir.'/'.$alias.'.txt');
			}
		}
		#update pfsense alias table
		if (is_array($config['aliases']['alias']))
			$aliases=$config['aliases']['alias'];
		foreach($aliases as $cbalias){
			if (preg_match("/pfBlocker/",$cbalias['name'])){
				#mark pfctl aliastable for cleaning
				if (!in_array($cbalias['name'], $aliases_list)) 
						$aliases_list[]=$cbalias['name']; #mark aliastable for cleaning
				#remove previous aliastable file if alias is not defined any more
				if (!in_array($cbalias['name'], $new_aliases_list))
					unlink_if_exists("/var/db/aliastables/".$cbalias['name'].".txt");
			}
			else{
				$new_aliases[]=	$cbalias;
				if (file_exists($pfb_alias_dir.'/'.$alias.'.txt') && $message ==""){
				preg_match("/(\d+)/",exec("/usr/bin/wc -l ".$pfb_alias_dir.'/'.$alias.'.txt'),$matches);
				}
				if (($matches[1] * 2.1)>= $table_limit )
					#alias table too large
    				$message= $alias .' alias table is too large. Reduce networks in list or increase "Firewall Maximum Table Entries" value to at least '. (int)($matches[1] * 2.1) .' in "system - advanced - Firewall/NAT".';
			}
		}
		#apply new alias table to xml
		if ($message == "")
			$config['aliases']['alias']=$new_aliases;
		#exit;	
	#############################################
	#          Assign rules 					#
	#############################################
	#print "<pre>";
	#var_dump($permit_inbound);
	#var_dump($permit_outbound);
	#var_dump($deny_inbound);
	#var_dump($deny_outbound);			
	#var_dump($pfblocker_config['inbound_interface']);
	#print count($deny_inbound) .count($deny_inbound);
	# Inbound filter options
	$inbound_interfaces = explode(",",$pfblocker_config['inbound_interface']);
	if (count($deny_inbound) > 0 || count($permit_inbound) > 0){
		if($pfblocker_config['inbound_interface'] == "")
			$message="Unable to apply rules.Inbound Interface option not configured.";
		 if (in_array("lo0",$inbound_interfaces))
			$message="Floating rules are not implemented in pfBlocker yet, choose Inbound Interface other than loopback or change action to Alias only.";
		}
		
	# Outbound filter options
	$outbound_interfaces = explode(",",$pfblocker_config['outbound_interface']);
	if (count($deny_outbound) > 0 || count($permit_outbound) > 0){
		if($pfblocker_config['outbound_interface'] == "")
			$message="Unable to apply rules.Outbound Interface option not configured.";
		 if (in_array("lo0",$outbound_interfaces))
			$message="Floating rules are not implemented in pfBlocker yet, choose Outbound Interface other than loopback or change action to Alias only.";
		}
		
	if ($message == "")
		{
		$last_iface="";
		$rules=$config['filter']['rule'];
		$new_rules=array();
		foreach ($rules as $rule){
			if ($rule['interface'] <> $last_iface){
				$last_iface = $rule['interface'];
				#apply pfblocker rules if enabled
				
				#Inbound
				foreach ($inbound_interfaces as $inbound_interface)
					if ($inbound_interface==$last_iface){
						#permit rules
						if (is_array($permit_inbound))
							foreach ($permit_inbound as $cb_rules){
								$cb_rules['interface']=$rule['interface'];
								$new_rules[]=$cb_rules;	
							}
						#deny rules
						if (is_array($deny_inbound))
							foreach ($deny_inbound as $cb_rules){
								$cb_rules['interface']=$rule['interface'];
								$new_rules[]=$cb_rules;	
							}
					}
				#Outbound
				foreach ($outbound_interfaces as $outbound_interface)
					if ($outbound_interface==$last_iface){
						#permit rules
						if (is_array($permit_outbound))
							foreach ($permit_outbound as $cb_rules){
								$cb_rules['interface']=$rule['interface'];
								$new_rules[]=$cb_rules;	
							}
						#deny rules
						if (is_array($deny_outbound))
							foreach ($deny_outbound as $cb_rules){
								$cb_rules['interface']=$rule['interface'];
								$new_rules[]=$cb_rules;	
							}
					}
			}
		  #include all rules that is not from pfBlocker
		  if (!preg_match("/pfBlocker.*rule/",$rule['descr']) && ($rule['interface'] != "" || $rule['floating']=="yes"))
					$new_rules[]=$rule;
		}
		$config['filter']['rule']=$new_rules;
		}	
	
	if ($message == ""){
		#check cron
		$cron_found=0;
		$cron_cmd="/usr/local/bin/php -q /usr/local/www/pfblocker.php cron";
		if (is_array($config['cron']['item'])){
			$new_cron=array();
			foreach($config['cron']['item'] as $cron){
					if (preg_match("/usr.local.www.pfblocker.php cron/",$cron["command"])){
						#fix 0.1.4.6 missing php path
						if($cron["command"]==$cron_cmd && $pfblocker_enable == "on"){
							$new_cron['item'][]=$cron;
							$cron_found=1;
							}
						}
					else{
						$new_cron['item'][]=$cron;
						}
				}
				if ($cron_found == 0){
					if($pfblocker_enable == "on")
						$new_cron['item'][]=array(	"minute" =>	"0",
													"hour"	 =>	"*",
													"mday" 	 =>	"*",
													"month"  =>	"*",
													"wday"	 =>	"*",
													"who"	 =>	"root",
													"command"=>	$cron_cmd);
						$config['cron']=$new_cron;
				}
		}
		
		# to be removed in final version
		$aliases_list[]="pfBlockerInbound"; #remove previous version lists
		$aliases_list[]="pfBlockerOutbound";#remove previous version lists
		$aliases_list[]="pfBlockerWL";		#remove previous version lists
		#exit;
		#update pfctrl tables
		foreach ($aliases_list as $table)
			exec("/sbin/pfctl -t " . escapeshellarg($table) . " -T kill 2>&1", $result_pfb);

		#uncheck donation and credits check box
		$config['installedpackages']['pfblocker']['config'][0]['donation']="";
		$config['installedpackages']['pfblocker']['config'][0]['credits']="";
		#write config
   		write_config();

		#update cron
		if ($cron_found == 0)
			configure_cron();
		
   		#load filter file after editing
		filter_configure();

		#sync config
		pfblocker_sync_on_changes();
		}
	else{
		log_error("[pfBlocker] ".$message);
		file_notice("pfBlocker", $message, "pfblocker rule apply", "");
	}
	conf_mount_ro();
	}
}

function pfblocker_validate_input($post, &$input_errors) {
	global $config;
	foreach ($post as $key => $value) {
		if (empty($value))
			continue;
		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 pfblocker_php_install_command() {
	include_once '/usr/local/www/pfblocker.php';
	pfblocker_get_countries();
	sync_package_pfblocker();
}

function pfblocker_php_deinstall_command() {
	global $config;
	$config['installedpackages']['pfblocker']['config'][0]['enable_cb']="";
	write_config();
	sync_package_pfblocker();
}

/* Uses XMLRPC to synchronize the changes to a remote node */
function pfblocker_sync_on_changes() {
	global $config, $g;
	log_error("[pfblocker] pfblocker_xmlrpc_sync.php is starting.");
	$synconchanges = $config['installedpackages']['pfblockersync']['config'][0]['synconchanges'];	
	if(!$synconchanges) 
		return;
	foreach ($config['installedpackages']['pfblockersync']['config'] as $rs ){
		foreach($rs['row'] as $sh){
		$sync_to_ip = $sh['ipaddress'];
		$password   = $sh['password'];
		if($password && $sync_to_ip)
			pfblocker_do_xmlrpc_sync($sync_to_ip, $password);
		}
	}
	log_error("[pfblocker] pfblocker_xmlrpc_sync.php is ending.");
}

/* Do the actual XMLRPC sync */
function pfblocker_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['pfblocker'] = $config['installedpackages']['pfblocker'];
	$xml['pfblockerlists'] = $config['installedpackages']['pfblockerlists'];
	$xml['pfblockertopspammers'] = $config['installedpackages']['pfblockertopspammers'];
	$xml['pfblockerafrica'] = $config['installedpackages']['pfblockerafrica'];
	$xml['pfblockerantartica'] = $config['installedpackages']['pfblockerantartica'];
	$xml['pfblockerasia'] = $config['installedpackages']['pfblockerasia'];
	$xml['pfblockereurope'] = $config['installedpackages']['pfblockereurope'];
	$xml['pfblockernorthamerica'] = $config['installedpackages']['pfblockernorthamerica'];
	$xml['pfblockeroceania'] = $config['installedpackages']['pfblockeroceania'];
	$xml['pfblockersouthamerica'] = $config['installedpackages']['pfblockersouthamerica'];
	/* 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 pfblocker 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 pfblocker XMLRPC sync with {$url}:{$port}.";
		log_error($error);
		file_notice("sync_settings", $error, "pfblocker Settings Sync", "");
	} elseif($resp->faultCode()) {
		$cli->setDebug(1);
		$resp = $cli->send($msg, "250");
		$error = "An error code was received while attempting pfblocker XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
		log_error($error);
		file_notice("sync_settings", $error, "pfblocker Settings Sync", "");
	} else {
		log_error("pfblocker XMLRPC sync successfully completed with {$url}:{$port}.");
	}
	
	/* tell pfblocker to reload our settings on the destionation sync host. */
	$method = 'pfsense.exec_php';
	$execcmd  = "require_once('/usr/local/pkg/pfblocker.inc');\n";
	$execcmd .= "sync_package_pfblocker();";
	
	/* assemble xmlrpc payload */
	$params = array(
		XML_RPC_encode($password),
		XML_RPC_encode($execcmd)
	);

	log_error("pfblocker 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 pfblocker XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
		log_error($error);
		file_notice("sync_settings", $error, "pfblocker Settings Sync", "");
	} elseif($resp->faultCode()) {
		$cli->setDebug(1);
		$resp = $cli->send($msg, "250");
		$error = "An error code was received while attempting pfblocker XMLRPC exec with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
		log_error($error);
		file_notice("sync_settings", $error, "pfblocker Settings Sync", "");
	} else {
		log_error("pfblocker XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
	}

}

?>