#!/usr/bin/perl -w
# Coondog 
# (c) 2006 Erik Meitner <emeitner@f2o.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#

use strict;
use Config::General;
use Gtk2 -init;
use Gtk2::GladeXML;
use Data::Dumper;
   
#####
### These will need to be configured for your system:

# path to the helper program. Remember: this program needs to be SUID root
my $helper_path = '/usr/bin/coondog-helper';
#my $helper_path = './coondog-helper.pl';

my $ifconfig_path = '/sbin/ifconfig';
my $route_path = '/sbin/route';

my $glade_dir = '/usr/share/coondog/glade';
#my $glade_dir = $ENV{'HOME'}."/dev/coondog-trunk/glade";

###
#####

my $VERSION = "0.1";

my $config_dir = $ENV{'HOME'}.'/.coondog/';
my $config_file = $config_dir.'coondog.conf';
my $racoon_config_file = $config_dir.'racoon.conf';
my $setkey_config = $config_dir.'setkey.conf';
my $racoon_psk_file = $config_dir.'psk.txt';

my $check_frequency = 2000; # Check this often(ms) for racoon status.
my $policy_check_frequency = 6000; # Check this often(ms) for vpn status.

my %configuration; # general application configuration
my %vpns; #list of vpns, configurations, etc.
my %vpninfo; # non configuration information on vpns, status, etc.
my $addeditmode; # 'edit' or 'add' , determines behavior of add/edit dialog
my $editoriginalname =''; # name of a config before edit window opened. used to check to see if user renamed config
my $racoon_pid=0; #pid of racoon, 0 if it is not running.
my $myip; # ip of interface that is configured with the default gateway
my $ipsec_state = 'stopped'; # 'stopped', 'started', 'starting', or 'stopping'
my $state_checks=0; # number of times racoon has been checked for being started/stopped
my $debug = 0; # turn on debug output. Very little available at this time.
my $quit_after_stop = 0; # set when user chooses to quit and stopvpnsonexit is set 

use constant VPNLIST_CHECK 	=> 1;
use constant VPNLIST_NAME 	=> 0;
use constant VPNLIST_STATUS	=> 2;

# Default configuration settings used if no file exists 
my %default_config = (
	'stopvpnsonexit' => 0
	,'logdebug' => 0
);

# default values for a new VPN configuration
my $default_vpn_name = 'name';
my %default_vpn_config = (
	'name' => 'name'
	,'mode' => 'host-net'	
	,'gateway' => ''
	,'remote' => ''
	,'local' => ''
	,'exchange_mode' => 'main'
	,'proposal_check' => 'obey'
	,'nonce' => 16
	,'p1lifetime' => 28800
	,'p1dhgroup' => 'dh1'
	,'p1encryption' => '3des'
	,'p1authentication' => 'sha1'
	,'p2lifetime' => '28800'
	,'p2pfsgroup' => 'none'
	,'p2encryption' => [ qw/3des/ ]
	,'p2authentication' => [ qw/hmac_sha1/ ]
	,'localidtype' => 'addr'
	,'localiddn' => ''
	,'remoteidtype' => 'addr'
	,'remoteiddn' => ''
	,'psk' => ''
	,'deflate' => 1
	,'doi' => 0
	,'sit' => 0
	,'vrfyid' => 0
	,'initial' => 1
	,'passive' => 0
	,'genpol' => 0
	,'supportproxy' => 0
	,'vrfycert' => 1
	,'dhcppt' => 0
	,'ikenow' => 0
);


#
# The following arrays are used to map GUI settings to/from config file options
# It's ugly, i know.

my @vpn_mode_string_mapping_long = (
	'Host to host(Transport)'
	,'Host to host(Tunnel)'
	,'Host to network'
	,'Network to network'
	,'Host to "anywhere"'
); 
my @vpn_mode_string_mapping_short = ( qw/host-host-transport host-host-tunnel host-net net-net host-any/ );

my @proposal_check_string_mapping_long = ( qw/Obey Strict Claim Exact/ );
my @proposal_check_string_mapping_short = map lc $_, @proposal_check_string_mapping_long;

my @p1_dhgroup_string_mapping_long = ( 'Mod768(1)', 'Mod1024(2)', 'Mod1536(5)' );
my @p1_dhgroup_string_mapping_short = ( qw/dh1 dh2 dh5/ );

my @p1_enc_string_mapping_long = ( qw/DES 3DES Blowfish Cast128 AES256 AES192 AES128/ );
my @p1_enc_string_mapping_short = map lc $_, @p1_enc_string_mapping_long;

my @p1_auth_string_mapping_long = ( qw/MD5 SHA1/ );
my @p1_auth_string_mapping_short = map lc $_, @p1_auth_string_mapping_long;

my @p2_enc_string_mapping_long =  ( 'DES', '3DES', 'DES IV64', 'DES IV32', 'AES 256', 'AES 192', 'AES 128', 'Blowfish', 'Cast 128', 'Null' );
my @p2_enc_string_mapping_short = ( 'des', '3des', 'des_iv64', 'des_iv32', 'aes_256', 'aes_192', 'aes_128', 'blowfish', 'cast_128', 'null_enc' );

my @p2_auth_string_mapping_long = ( 'HMAC MD5' , 'HMAC SHA1', 'None' );
my @p2_auth_string_mapping_short = ( 'hmac_md5', 'hmac_sha1', 'non_auth' );

my @p2_pfsgroup_string_mapping_long = ( 'Mod768(1)', 'Mod1024(2)', 'Mod1536(5)', 'None');
my @p2_pfsgroup_string_mapping_short = ( qw/dh1 dh2 dh5 none/ );


#my @options_string_mapping_long = ( );
my @options_string_mapping_short = ( qw/deflate doi sit vrfyid initial passive genpol supportproxy vrfycert dhcppt ikenow/ );

my @id_types_string_mapping_long = ( qw/address user_fqdn fqdn keyid/ );
my @id_types_string_mapping_short = ( qw/addr dn udn cert/ );

#my @_string_mapping_long = ( );
#my @_string_mapping_short = ( );

########################################################################
########################################################################
########################################################################
# Start this thing.
########################################################################
########################################################################
########################################################################

my $gladexml = Gtk2::GladeXML->new( "$glade_dir/coondog.glade" );

$gladexml->get_widget( "aboutdialog" )->set_version( $VERSION );
$gladexml->get_widget( "aboutdialog" )->set_modal( 1 );

$gladexml->signal_autoconnect_from_package('main');

Gtk2->init_add( \&initialize_stuff );

Glib::Timeout->add ( $check_frequency , \&check_racoon_status);

Gtk2->main;

########################################################################
########################################################################
########################################################################
# Subroutines follow....
########################################################################
########################################################################
########################################################################

sub initialize_stuff{
	
	my $configtreeview = $gladexml->get_widget( "configtreeview" );
	my $name_renderer = Gtk2::CellRendererText->new;
	my $state_renderer = Gtk2::CellRendererText->new;
	my $toggle_renderer = Gtk2::CellRendererToggle->new;
	$toggle_renderer->set_property('activatable', 1);
	$toggle_renderer->set_property("inconsistent", 0);
	$toggle_renderer->set_property("radio", 0);
	
	my $column1 =  Gtk2::TreeViewColumn->new_with_attributes ("Name", $name_renderer, "text", VPNLIST_NAME );
	my $column2 =  Gtk2::TreeViewColumn->new_with_attributes ("Enabled", $toggle_renderer,"active", VPNLIST_CHECK );
	my $column3 =  Gtk2::TreeViewColumn->new_with_attributes ("Status", $state_renderer, "text", VPNLIST_STATUS );
	
	my $liststore = Gtk2::ListStore->new ( 'Glib::String', 'Glib::Boolean', 'Glib::String' );
	$configtreeview->set_model($liststore);
	$configtreeview->append_column ($column1);
	$configtreeview->append_column ($column2);	
	$configtreeview->append_column ($column3);	

	$toggle_renderer->signal_connect( "toggled", \&vpn_toggled, $gladexml->get_widget('configtreeview')->get_model );
		
	$gladexml->get_widget( "addeditwindow" )->set_transient_for($gladexml->get_widget( "window" ));
	$gladexml->get_widget( "configwindow" )->set_transient_for($gladexml->get_widget( "window" ));
		
	perform_startup_checks();
	load_general_config();
	load_vpn_configs();
	fill_vpn_list();

	$myip = get_ip(get_main_interface());
	
	#check to see if racoon is running
	$racoon_pid = get_racoon_pid();
	if( $racoon_pid > 0 ){
		if( ! defined $myip ){  # maybe the interface is now down?
			stop_racoon();
		}else{
			$ipsec_state = 'started';
			$gladexml->get_widget('vpnaddbutton')->set_sensitive(0);
			$gladexml->get_widget('vpneditbutton')->set_sensitive(0);
			$gladexml->get_widget('vpnremovebutton')->set_sensitive(0);
			$gladexml->get_widget('configtreeview')->set_sensitive(0);
			prepare_vpn_status_info();
			ipsecbutton_on();
			check_vpn_states();
		}
	}else{
		system ( $helper_path, "cleanup" );
	}
}


sub perform_startup_checks {
	
	#check configuration directory	
	if( ! -d $config_dir ){
		$!=undef;
		mkdir $config_dir, 00700;
		show_error_dialog( "Problems creating configuration directory '$config_dir': $!", 1 ) if( $! );
	}
	
	show_error_dialog( "Unable to set secure permissions on configuration directory. Permissions on $config_dir should be 0700.", 1 )
		if( ! check_config_dir_permissions() );
		
	#check for coondog-helper, and permissions
	if( ! -f $helper_path ){
		show_error_dialog( "Unable to find the helper program at: $helper_path", 1 );
	}
	my ($helper_perms,$helper_owner,$helper_group) = (stat($helper_path))[2,4,5];
	if( $helper_owner != 0 or $helper_group != 0 ){
		show_error_dialog( "Helper program must be owned by user 'root', group 'root'.", 1 );
	}
	if( ($helper_perms & 00700) < 0700){
		show_error_dialog( "Helper program is not executable by 'root' user.", 1 );
	}
	if( ($helper_perms & 00002) > 0 or ($helper_perms & 00020) > 0 ){
		show_error_dialog( sprintf("Helper program permissions are insecure: %04o",$helper_perms &07777), 1 );
	}	
	if( ($helper_perms & 07000) != 04000){
		show_error_dialog( "Helper program must be set SUID root.", 1 );
	}
}

# also called before racoon is started
sub check_config_dir_permissions {
	my $perms = (stat($config_dir))[2] & 07777;
	if( $perms != 00700 ) {
		return 0 if( ! chmod(0700, $config_dir) );
		show_error_dialog( "There were insecure permissions on configuration directory. Fixed.", 0 );
	}
	return 1;
}
	

########################################################################
# Application control
########################################################################


sub on_quitbutton_clicked {
	if( $configuration{'stopvpnsonexit'} and $ipsec_state eq 'started' ){
		$quit_after_stop = 1;
		on_ipsecbutton_clicked();
	}
	exit;
}

sub on_aboutbutton_clicked {
	my $about_dialog = $gladexml->get_widget( "aboutdialog" );
	$about_dialog->show;
}


sub on_window_delete_event {
	on_quitbutton_clicked();
}



########################################################################
# General config
########################################################################

sub on_configbutton_clicked {
	my $config_dialog = $gladexml->get_widget( 'configwindow' );
	
	$gladexml->get_widget( 'stopvpnsonexitcheckbutton' )->set_active( $configuration{'stopvpnsonexit'} );
	$gladexml->get_widget( 'debugcheckbutton' )->set_active( $configuration{'logdebug'} );
	$gladexml->get_widget( 'checkvpnstatecheckbutton' )->set_active( $configuration{'monitorvpns'} );
	
	$config_dialog->show;
}

sub load_general_config {

	if( -f $config_file ){
		%configuration = ParseConfig( -ConfigFile => $config_file ,-AutoTrue => 1 ); #, -DefaultConfig => \%default_config );		
		show_error_dialog( "There was a problem reading the configuration from: $config_file", 1 ) if( ! %configuration );
	}else{
		%configuration = %default_config;
		SaveConfig( $config_file, \%configuration );
	}
}

sub on_configcancelbutton_clicked {
	my $config_dialog = $gladexml->get_widget( "configwindow" );
	$config_dialog->hide;
}

sub on_configsavebutton_clicked {
	apply_config();
	SaveConfig( $config_file, \%configuration );
	on_configcancelbutton_clicked;
}

sub apply_config {
	my %newconf;

	$newconf{'stopvpnsonexit'} = $gladexml->get_widget( "stopvpnsonexitcheckbutton" )->get_active || 0;
	$newconf{'logdebug'} = $gladexml->get_widget( "debugcheckbutton" )->get_active || 0;

	$newconf{'monitorvpns'} = $gladexml->get_widget( "checkvpnstatecheckbutton" )->get_active || 0;
	show_error_dialog('Note: The "Monitor VPNS" option is not recommended at this time.', 0) if $newconf{'monitorvpns'};

	%configuration = %newconf;	
}

sub on_configwindow_delete_event {
	on_configcancelbutton_clicked();
	return 1;
}



########################################################################
# dialogs
########################################################################

sub show_error_dialog {
	my ($msg, $die_too ) = @_;
	my $dialog = Gtk2::MessageDialog->new ( $gladexml->get_widget( "window" ), 'modal', 'error', 'ok', "%s", $msg);
	$dialog->run;
	$dialog->destroy;
	exit(1) if $die_too;
}
 
sub yesno_dialog {
	my $msg = shift;
	my $dialog = Gtk2::Dialog->new ('Question', $gladexml->get_widget( 'addeditwindow' ),
		[qw/destroy-with-parent modal/],'gtk-yes' => 'yes', 'gtk-no' => 'no' );
		my $label = Gtk2::Label->new ( $msg );
	$dialog->vbox->add ($label);
	$label->show;
	my $ret = $dialog->run;
	$dialog->destroy;
	return $ret;
 }
 
########################################################################
# VPN configuration editing, creation, deletion
########################################################################

sub apply_vpn_configuration {
	my $vpnconfig = shift;
	my $name = shift;
	
	# vpn type tab
	$gladexml->get_widget( 'vpnnameentry' )->set_text( $name );
	$gladexml->get_widget( 'modecombobox' )->set_active( mapping_get_long( 'vpn_mode', $vpnconfig->{'mode'}, 1) );
	
	$gladexml->get_widget( 'remotegateentry' )->set_text( $vpnconfig->{'gateway'} );

	my $remote = $vpnconfig->{'remote'};
	if( $remote){
		my $remotecidr;
		( $remote, $remotecidr ) = ($1,$2) if ( $vpnconfig->{'remote'} =~ m#^(\d+\.\d+\.\d+\.\d+)/(\d+)$# ); 
		$gladexml->get_widget( 'remoteentry' )->set_text( $remote );
		$gladexml->get_widget( 'remoteentrycidr' )->set_text( $remotecidr ) if $remotecidr;
	}
	
	my $local = $vpnconfig->{'local'};
	if( $local ){
		my $localcidr;
		( $local, $localcidr ) = ($1,$2) if ( $vpnconfig->{'local'} =~ m#^(\d+\.\d+\.\d+\.\d+)/(\d+)$# ); 
		$gladexml->get_widget( 'localentry' )->set_text( $local );
		$gladexml->get_widget( 'localentrycidr' )->set_text( $localcidr ) if $localcidr;
	}
	
	$gladexml->get_widget( $vpnconfig->{'exchange_mode'}.'exmoderadiobutton' )->set_active(1);
	$gladexml->get_widget( 'proposalcheckcombobox' )->set_active( mapping_get_long( 'proposal_check', $vpnconfig->{'proposal_check'}, 1) );
	$gladexml->get_widget( 'nonceentry' )->set_text( $vpnconfig->{'nonce'} );
	
	# phase 1 tab
	$gladexml->get_widget( 'p1lifetimeentry' )->set_text( $vpnconfig->{'p1lifetime'} );
	$gladexml->get_widget( 'p1dhgroupcombobox' )->set_active( mapping_get_long( 'p1_dhgroup', $vpnconfig->{'p1dhgroup'}, 1) );
	$gladexml->get_widget( 'p1enccombobox' )->set_active( mapping_get_long( 'p1_enc', $vpnconfig->{'p1encryption'}, 1) );
	$gladexml->get_widget( 'p1authcombobox' )->set_active( mapping_get_long( 'p1_auth', $vpnconfig->{'p1authentication'}, 1) );
	
	# phase 2 tab
	$gladexml->get_widget( 'p2lifetimeentry' )->set_text( $vpnconfig->{'p1lifetime'} );
	$gladexml->get_widget( 'p2pfsgroupcombobox' )->set_active( mapping_get_long( 'p1_dhgroup', $vpnconfig->{'p1dhgroup'}, 1) );

	foreach my $p2enc ( @p2_enc_string_mapping_short ){
		$gladexml->get_widget( 'p2enc-'.$p2enc.'-checkbutton' )->set_active( scalar grep(/^$p2enc$/, @{$vpnconfig->{'p2encryption'}} ));
	}

	foreach my $p2auth ( @p2_auth_string_mapping_short ){
		$gladexml->get_widget( 'p2auth-'.$p2auth.'-checkbutton' )->set_active( scalar grep(/^$p2auth$/, @{$vpnconfig->{'p2authentication'}} ));
	}	

	# auth/id tab
	$gladexml->get_widget( 'lid'.$vpnconfig->{'localidtype'}.'radiobutton' )->set_active(1);
	$gladexml->get_widget( 'rid'.$vpnconfig->{'remoteidtype'}.'radiobutton' )->set_active(1);
	$gladexml->get_widget( 'liddnentry' )->set_text( ($vpnconfig->{'localidtype'} eq 'dn' ) ? $vpnconfig->{'localiddn'}  : '' );
	$gladexml->get_widget( 'riddnentry' )->set_text( ($vpnconfig->{'remoteidtype'} eq 'dn' ) ? $vpnconfig->{'remoteiddn'}  : '' ) ;
	$gladexml->get_widget( 'pskentry' )->set_text( $vpnconfig->{'psk'} );

	# options tab
	foreach my $option ( @options_string_mapping_short ){
		$gladexml->get_widget( 'opt-'.$option.'-checkbutton' )->set_active( $vpnconfig->{ $option } );
	}
	on_liddnradiobutton_toggled();
	on_riddnradiobutton_toggled();
}


sub get_vpn_configuration {
	my %cfg;	
	
	# vpn type tab
	my $name = $gladexml->get_widget( 'vpnnameentry' )->get_text;

	if(! ($name =~ /^\s*([\w -]+)(?<![ ])\s*$/i) ){
		show_error_dialog("Connection Name may contain only letters, numbers, '-', '_', and spaces.", 0);
		return undef;
	}
	$name = $1;
	
	$cfg{$name} = {};
	$cfg{$name}->{'mode'} = mapping_get_short( 'vpn_mode', $gladexml->get_widget( 'modecombobox' )->get_active_text);
	my $gate = $gladexml->get_widget( 'remotegateentry' )->get_text;
	my $remote = $gladexml->get_widget( 'remoteentry' )->get_text;
	my $remotecidr = $gladexml->get_widget( 'remoteentrycidr' )->get_text;
	my $local = $gladexml->get_widget( 'localentry' )->get_text;
	my $localcidr = $gladexml->get_widget( 'localentrycidr' )->get_text;
	
	if( ! is_valid_ip( $gate ) ){
		show_error_dialog("Not a valid IP address for Remote Gateway.", 0);
		return undef;
	}

	$cfg{$name}->{'gateway'} = $gate;

	if( $cfg{$name}->{'mode'} eq 'host-host-transport' ){
		# nothing do do
	}elsif ( $cfg{$name}->{'mode'} eq 'host-host-tunnel' ){
		if( $local and ! is_valid_ip( $local ) ){
			show_error_dialog("Not a valid IP address for Local Host.", 0);
			return undef;
		}
		if( $remote and ! is_valid_ip( $remote ) ){
			show_error_dialog("Not a valid IP address for Remote Host.", 0);
			return undef;
		}
		$cfg{$name}->{'local'} = $local if ( $local ) ;
		$cfg{$name}->{'remote'} = $remote if ( $remote );
	}elsif ( $cfg{$name}->{'mode'} eq 'host-net' ){
		if( $local and ! is_valid_ip( $local ) ){
			show_error_dialog("Not a valid IP address for Local Host.", 0);
			return undef;
		}
		if( ! is_valid_cidr( "$remote/$remotecidr" ) ){
			show_error_dialog("Not a valid IP address for Remote Net.", 0);
			return undef;
		}
		$cfg{$name}->{'local'} = $local if ( $local ) ;
		$cfg{$name}->{'remote'} = "$remote/$remotecidr";
	}elsif ( $cfg{$name}->{'mode'} eq 'net-net' ){	
		if( ! is_valid_cidr( "$local/$localcidr" ) ){
			show_error_dialog("Not a valid IP address for Local Net.", 0);
			return undef;
		}
		if( ! is_valid_cidr( "$remote/$remotecidr" ) ){
			show_error_dialog("Not a valid IP address for Remote Net.", 0);
			return undef;
		}
		$cfg{$name}->{'local'} = "$local/$localcidr";
		$cfg{$name}->{'remote'} = "$remote/$remotecidr";
	}elsif ( $cfg{$name}->{'mode'} eq 'host-any' ){
		# nothing to do
	}		


	$cfg{$name}->{'nonce'} = $gladexml->get_widget( 'nonceentry' )->get_text;
	if( $cfg{$name}->{'nonce'} <8 or $cfg{$name}->{'nonce'} > 255 ){
		show_error_dialog("Not a valid value for Nonce Size.", 0);
		return undef;
	}
		
	# get exchange mode
	foreach my $exwidget ( qw/base main aggressive/ ){
		if( $gladexml->get_widget( $exwidget.'exmoderadiobutton' )->get_active ){
			$cfg{$name}->{'exchange_mode'} = $exwidget;
			last;
		}
	}
	$cfg{$name}->{'proposal_check'} = mapping_get_short( 'proposal_check' ,$gladexml->get_widget( 'proposalcheckcombobox' )->get_active_text);

	# phase 1 tab
	$cfg{$name}->{'p1lifetime'} = $gladexml->get_widget( 'p1lifetimeentry' )->get_text;
	if( $cfg{$name}->{'p1lifetime'} < 0 ){
		show_error_dialog("Not a valid value for Phase 1 Lifetime.", 0);
		return undef;
	}
	
	$cfg{$name}->{'p1dhgroup'} = mapping_get_short( 'p1_dhgroup', $gladexml->get_widget( 'p1dhgroupcombobox' )->get_active_text);
	$cfg{$name}->{'p1encryption'} = mapping_get_short( 'p1_enc', $gladexml->get_widget( 'p1enccombobox' )->get_active_text);
	$cfg{$name}->{'p1authentication'} = mapping_get_short( 'p1_auth', $gladexml->get_widget( 'p1authcombobox' )->get_active_text);
	
	#phase 2 tab
	$cfg{$name}->{'p2lifetime'} = $gladexml->get_widget( 'p2lifetimeentry' )->get_text;
	if( $cfg{$name}->{'p2lifetime'} < 0 ){
		show_error_dialog("Not a valid value for Phase 2 Lifetime.", 0);
		return undef;
	}
	$cfg{$name}->{'p2pfsgroup'} = mapping_get_short( 'p2_pfsgroup', $gladexml->get_widget( 'p2pfsgroupcombobox' )->get_active_text);
	
	$cfg{$name}->{'p2encryption'} = [];
	foreach my $p2enc ( @p2_enc_string_mapping_short ){
		push @{$cfg{$name}->{'p2encryption'}}, $p2enc if( $gladexml->get_widget( 'p2enc-'.$p2enc.'-checkbutton' )->get_active );
	}
	$cfg{$name}->{'p2authentication'} = [];
	foreach my $p2auth ( @p2_auth_string_mapping_short ){
		push @{$cfg{$name}->{'p2authentication'}}, $p2auth if( $gladexml->get_widget( 'p2auth-'.$p2auth.'-checkbutton' )->get_active );
	}
		
	# auth/id tab
	foreach my $idwidget ( qw/addr cert dn/ ){
		$cfg{$name}->{'localidtype'} = $idwidget if( $gladexml->get_widget( 'lid'.$idwidget.'radiobutton' )->get_active );
		$cfg{$name}->{'remoteidtype'} = $idwidget if( $gladexml->get_widget( 'rid'.$idwidget.'radiobutton' )->get_active );
	}
	
	if( $cfg{$name}->{'localidtype'} eq 'dn' ){
		$cfg{$name}->{'localiddn'} = $gladexml->get_widget( 'liddnentry' )->get_text ;
		if( ! $cfg{$name}->{'localiddn'} ){
			show_error_dialog("Local DN must be non-empty.", 0);
			return undef;
		}	
	}
	
	if( $cfg{$name}->{'remoteidtype'} eq 'dn' ){
		$cfg{$name}->{'remoteiddn'} = $gladexml->get_widget( 'riddnentry' )->get_text ;
		if( ! $cfg{$name}->{'remoteiddn'} ){
			show_error_dialog("Remote DN must be non-empty.", 0);
			return undef;
		}	
	}
	$cfg{$name}->{'psk'} = $gladexml->get_widget( 'pskentry' )->get_text;
	if( ! $cfg{$name}->{'psk'} ){
		show_error_dialog("Pre-shared Key must be non-empty.", 0);
		return undef;
	}
		
	# options tab 
	foreach my $option ( @options_string_mapping_short ){
		$cfg{$name}->{$option} = $gladexml->get_widget( 'opt-'.$option.'-checkbutton' )->get_active ;
	}
	
	return \%cfg;
}

sub on_liddnradiobutton_toggled {
	$gladexml->get_widget( 'liddnentry' )->set_sensitive( $gladexml->get_widget( 'liddnradiobutton' )->get_active );
}
sub on_riddnradiobutton_toggled {
	$gladexml->get_widget( 'riddnentry' )->set_sensitive( $gladexml->get_widget( 'riddnradiobutton' )->get_active );
}

# if mode is aggressive, phase-2 dhgroup must = phase-1 dhgroup
sub on_p1dhgroupcombobox_changed {
	if( $gladexml->get_widget( 'aggressiveexmoderadiobutton' )->get_active ){
		my $dhg = mapping_get_short( 'p1_dhgroup', $gladexml->get_widget( 'p1dhgroupcombobox' )->get_active_text);
		$gladexml->get_widget( 'p2pfsgroupcombobox' )->set_active( mapping_get_long( 'p1_dhgroup', $dhg, 1) );
		
	}
}
#make sure that p2 dh combobox is sensitive if exch mode not aggressive
sub on_aggressiveexmoderadiobutton_toggled {
	if( $gladexml->get_widget( 'aggressiveexmoderadiobutton' )->get_active ){
		on_p1dhgroupcombobox_changed();
		$gladexml->get_widget( 'p2pfsgroupcombobox' )->set_sensitive(0);
	}else{
		$gladexml->get_widget( 'p2pfsgroupcombobox' )->set_sensitive(1);
	}
}

sub on_vpnaddbutton_clicked {
	apply_vpn_configuration( \%default_vpn_config, $default_vpn_name );
	$addeditmode = 'add';
	$gladexml->get_widget( 'addeditnotebook' )->set_current_page(0);
	$gladexml->get_widget( 'addeditwindow' )->show;
	$editoriginalname='';
}

sub on_vpneditbutton_clicked {
	my %vpn_config;
	my $name;
	my $configtreeview = $gladexml->get_widget('configtreeview');
	my $iter = get_active_item_iter();
	if( defined $iter ){
		my $model = $configtreeview->get_model;
		( $name ) = $model->get($iter,(VPNLIST_NAME));
	}else{
		show_error_dialog('No VPN selected.',0);
		return;
	}
	$editoriginalname = $name;
	apply_vpn_configuration( \%default_vpn_config , $default_vpn_name );
	apply_vpn_configuration( $vpns{$name}, $name );
	$gladexml->get_widget( 'addeditnotebook' )->set_current_page(0);
	$addeditmode = 'edit';
	$gladexml->get_widget( 'addeditwindow' )->show;
}


sub on_vpnremovebutton_clicked {
	my $configtreeview = $gladexml->get_widget('configtreeview');
	my $iter = get_active_item_iter();
	if( defined $iter ){
		my $model = $configtreeview->get_model;
		my  $name = $model->get($iter,VPNLIST_NAME);
		my $yn = yesno_dialog( "Do you wish to remove the configuration for the VPN named '$name'?" );
		if( $yn eq 'yes' ){
			delete_vpn_config( $name );
			delete $vpns{$name};
			unlink "$config_dir$name.vpn";
		}
	}else{
		show_error_dialog('No VPN selected.',0);
	}
	return 1;
}

# Remove named vpn(arg 0) from vpn list
sub delete_vpn_config {
	my $name = shift;
	my $configtreeview = $gladexml->get_widget('configtreeview');
	my $treemodel = $configtreeview->get_model;
	my $iter = $treemodel->get_iter_first;
	while(1){
		my  $n  = $treemodel->get($iter, VPNLIST_NAME );
		if( $name eq $n ){
			$treemodel->remove( $iter );
			last;	
		}
		last if ! defined ($iter = $treemodel->iter_next($iter) );
	}
	delete $vpns{$name};
	unlink "$config_dir$name.vpn";
}

sub on_addeditwindow_delete_event {
	my $window = $gladexml->get_widget( 'addeditwindow' )->hide;
	$editoriginalname='';
	return 1;
}

sub on_vpnsavebutton_clicked {
	my $vpncfg = get_vpn_configuration();

	if( defined $vpncfg ){

		my ($name) = keys %{$vpncfg};

		$vpncfg->{$name}->{'enabled'} = $vpns{$name}->{'enabled'} ? 1 : 0;		
		$vpns{$name} = $vpncfg->{$name};

		my $configfilename = "$config_dir$name.vpn";
		if( $addeditmode eq 'add' and -e $configfilename ){
			if(yesno_dialog( 'A configuration by that name already exists. Overywrite it?' ) eq 'no' ){
				$gladexml->get_widget( 'vpnnameentry' )->grab_focus;
				return 1;
			}else{
				$addeditmode = 'edit';
			}
		}

		# user renamed config, delete old one
		if( $editoriginalname and $name ne $editoriginalname ){
			delete_vpn_config( $editoriginalname );
			$addeditmode = 'add';
		}
		
		if( ! SaveConfig( $configfilename, $vpncfg ) ){
			show_error_dialog("Unable to save VPN configuation: $!", 0);
		}else{
			if( $addeditmode eq 'add' ){
				fill_vpn_list();
			}
			$gladexml->get_widget( 'addeditwindow' )->hide;
		}
	}
	$editoriginalname='';
	return 1;
}

sub on_vpncancelbutton_clicked {
	$editoriginalname = '';
	on_addeditwindow_delete_event();
}

sub on_modecombobox_changed {
	my $widget = shift;
	my $mode = mapping_get_short( 'vpn_mode', $widget->get_active_text );
	if( $mode eq 'host-host-transport' ){
		$gladexml->get_widget( 'remotelabel' )->hide;
		$gladexml->get_widget( 'locallabel' )->hide;
		$gladexml->get_widget( 'remoteentry' )->hide;
		$gladexml->get_widget( 'remoteentrycidr' )->hide;
		$gladexml->get_widget( 'remotecidrslashlabel' )->hide;
		$gladexml->get_widget( 'localentry' )->hide;
		$gladexml->get_widget( 'localentrycidr' )->hide;
		$gladexml->get_widget( 'localcidrslashlabel' )->hide;
	}elsif ( $mode eq 'host-host-tunnel' ){
		$gladexml->get_widget( 'remotelabel' )->show;
		$gladexml->get_widget( 'remotelabel' )->set_text('Remote host(optional):');		
		$gladexml->get_widget( 'locallabel' )->show;
		$gladexml->get_widget( 'locallabel' )->set_text('Local host(optional):');
		$gladexml->get_widget( 'remoteentry' )->show;
		$gladexml->get_widget( 'remoteentrycidr' )->hide;
		$gladexml->get_widget( 'remotecidrslashlabel' )->hide;
		$gladexml->get_widget( 'localentry' )->show;
		$gladexml->get_widget( 'localentrycidr' )->hide;
		$gladexml->get_widget( 'localcidrslashlabel' )->hide;
	}elsif ( $mode eq 'host-net' ){
		$gladexml->get_widget( 'remotelabel' )->show;
		$gladexml->get_widget( 'remotelabel' )->set_text('Remote Net:');
		$gladexml->get_widget( 'locallabel' )->show;
		$gladexml->get_widget( 'locallabel' )->set_text('Local host(optional):');
		$gladexml->get_widget( 'remoteentry' )->show;
		$gladexml->get_widget( 'remoteentrycidr' )->show;
		$gladexml->get_widget( 'remotecidrslashlabel' )->show;
		$gladexml->get_widget( 'localentry' )->show;
		$gladexml->get_widget( 'localentrycidr' )->hide;
		$gladexml->get_widget( 'localcidrslashlabel' )->hide;
	}elsif ( $mode eq 'net-net' ){	
		$gladexml->get_widget( 'remotelabel' )->show;
		$gladexml->get_widget( 'remotelabel' )->set_text('Remote Net:');
		$gladexml->get_widget( 'locallabel' )->show;
		$gladexml->get_widget( 'locallabel' )->set_text('Local Net:');
		$gladexml->get_widget( 'remoteentry' )->show;
		$gladexml->get_widget( 'remoteentrycidr' )->show;
		$gladexml->get_widget( 'remotecidrslashlabel' )->show;
		$gladexml->get_widget( 'localentry' )->show;
		$gladexml->get_widget( 'localentrycidr' )->show;
		$gladexml->get_widget( 'localcidrslashlabel' )->show;
	}elsif ( $mode eq 'host-any' ){
		$gladexml->get_widget( 'remotelabel' )->hide;
		$gladexml->get_widget( 'locallabel' )->hide;
		$gladexml->get_widget( 'remoteentry' )->hide;
		$gladexml->get_widget( 'remoteentrycidr' )->hide;
		$gladexml->get_widget( 'remotecidrslashlabel' )->hide;
		$gladexml->get_widget( 'localentry' )->hide;
		$gladexml->get_widget( 'localentrycidr' )->hide;
		$gladexml->get_widget( 'localcidrslashlabel' )->hide;
	}		
}


# create the starting vpn list and put it in the treeview
sub fill_vpn_list {
	my $ctv = $gladexml->get_widget('configtreeview');
	my $configtreemodel = $ctv->get_model;
	$configtreemodel->clear;
	my $iter = $configtreemodel->get_iter_first;
	foreach my $vpn ( sort {return lc($a) cmp lc($b) } keys %vpns ){
		$iter = $configtreemodel->append;
		$configtreemodel->set ($iter, VPNLIST_NAME, $vpn,VPNLIST_CHECK, $vpns{$vpn}->{'enabled'}, VPNLIST_STATUS, 'Off' );
	}
	set_active_item( 0 );
}

sub set_active_item {
	my $iter = shift;
	my $configtreeview = $gladexml->get_widget('configtreeview');
	my $configtreemodel = $configtreeview->get_model;
	if( ! $iter ){
		$iter = $configtreemodel->get_iter_first;
	}
	return if( ! $iter );
	
	my $path = $configtreemodel->get_path($iter);
	$configtreeview->set_cursor ($path, undef,0);
}

sub get_active_item_iter {
	my $configtreeview = $gladexml->get_widget('configtreeview');
	my ($path, $focus_column) = $configtreeview->get_cursor;
	return undef if !$path;
	my $model = $configtreeview->get_model;
	my $iter=$model->get_iter( $path );
	return $iter;
}

sub vpn_toggled{
	my ($cell, $path, $model) = @_;	
	my $iter = $model->get_iter_from_string( $path );
	my $enabled = $model->get ($iter, VPNLIST_CHECK) ? 0 : 1;
	$model->set ($iter, VPNLIST_CHECK, $enabled );
	my $name  = $model->get($iter,VPNLIST_NAME);
	$vpns{$name}->{'enabled'} = $enabled;
	my %cfg;
	$cfg{$name} = $vpns{$name};
	if( ! SaveConfig( "$config_dir$name.vpn", \%cfg  ) ){
		show_error_dialog("Unable to save VPN configuation: $!", 0);
	}
}


########################################################################
# Loading/saving VPN configs
########################################################################

sub load_vpn_configs {
	my @configs;
	opendir VPNCFGS, $config_dir;
	while ( $_ = readdir VPNCFGS){
		next if /^\.\.?$/;
		push @configs, $1 if /^([\w -]+)\.vpn$/;
	}
	closedir VPNCFGS;

	foreach my $name ( @configs ){
		my %conf = ParseConfig( -ConfigFile=> "$config_dir$name.vpn" );
		$vpns{$name} = $conf{$name};

		if(ref($vpns{$name}->{'p2encryption'}) ne "ARRAY") {
			$vpns{$name}->{'p2encryption'} = [$vpns{$name}->{'p2encryption'}];
		}
	 	if(ref($vpns{$name}->{'p2authentication'}) ne "ARRAY") {
			$vpns{$name}->{'p2authentication'} = [$vpns{$name}->{'p2authentication'}];
		}
		
	}

}

# see: on_vpnsavebutton_clicked()

########################################################################
# Misc utility functions
########################################################################

# given the short string, return the long one

sub mapping_get_long {
	my ($mappingname, $value, $getindex) = @_;
	my @short = eval "\@".$mappingname."_string_mapping_short";
	for(my $i=0;$i<@short;$i++){
		if ( $short[$i] eq $value ){
			return $i if $getindex;
			return eval "\$".$mappingname."_string_mapping_long[$i]";	
		}
	}
}

# given the long string, return the short one
sub mapping_get_short {
	my ($mappingname, $value, $getindex) = @_;
	my @long = eval "\@".$mappingname."_string_mapping_long";
	for(my $i=0;$i<@long;$i++){
		if ( $long[$i] eq $value ){
			return $i if $getindex;
			return eval "\$".$mappingname."_string_mapping_short[$i]";
			
		}
	}
}

# do a basic sanity check on IP addresses
sub is_valid_ip {
	my $ip = shift;
	return 0 if( ! ( $ip =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) );
	my @octets = split '\.', $ip;
	foreach my $oct ( @octets ){
		return 0 if( $oct > 255 or $oct < 0);
	}
	return 1;	
}

# do a basic sanity check on a CIDR format network address
sub is_valid_cidr {
	my $cidr = shift;
	my($ip, $net) = split '/', $cidr;
	return 0 if ! is_valid_ip( $ip );
	return 0 if( ! $net or $net > 32 or $net < 0 );
	return 1;	
}

#
# Get the name of the interface that is used to get to the default gateway
# and return it.
sub get_main_interface {
	my $srch_pat;
	my $routes;

	$srch_pat = '^0\.0\.0\.0\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(\S+)';
	$routes = `$route_path -n 2>&1`;
	show_error_dialog( $routes,0 ) if( $? );

	$routes =~ /$srch_pat/m;
	if( ! $1 ) {
		show_error_dialog( "Can't find the primary interface. Is the network connection up?\n",0 ) ;
		return undef;
	}
	return $1;
}

#
# Get the IP of the interface $if and return it.
#
sub get_ip {
	my $if = shift;
	my $ip;
	my $ip_srch;
	my  $if_srch;

	return undef if !defined $if;
	
	my $iflist = `$ifconfig_path $if 2>&1`;
	show_error_dialog( $iflist, 0) if( $? );
	my @lines = split "\n",$iflist;

	my $got_if = 0;

	$ip_srch = 'inet addr:(\S+)';
	$if_srch = "^$if\\s+";

	foreach my $line ( @lines ){
		$got_if = 1 if( $line =~ /$if_srch/ );
		last if( $line =~ /^\s+$/ and $got_if);
		return $1 if( $got_if and $line =~ /$ip_srch/ );
	}
	show_error_dialog("Could not get IP address for interface $if.\n",0);
	return undef;
}

########################################################################
# Racoon control
########################################################################

sub on_ipsecbutton_clicked {
	my $err;
	$gladexml->get_widget('ipsecbutton')->set_sensitive(0);
	if( $ipsec_state eq 'stopped'  ){ 
		$err = start_racoon();
		if( ! $err ){
			$ipsec_state='starting' 
		}else{
			my $msg = "There was a problem starting IPSec. ";
			$err='' if $err == 1;#no text error message
			$msg .= $err;
			show_error_dialog($msg, 0);
			system("$helper_path", "cleanup");
			show_error_dialog( "Unable to remove temporary configuration files.($?)") if $?;
		}
	}elsif($ipsec_state eq 'started'){
		$err = stop_racoon();
		if( ! $err ){
			$ipsec_state='stopping';
		}else{
			my $msg = "There was a problem stopping IPSec. ";
			$err='' if $err == 1;#no text error message
			$msg .= $err;
			show_error_dialog( $msg, 0);
		}
	}
	$gladexml->get_widget('ipsecbutton')->set_sensitive(1);
}

sub ipsecbutton_on {
	my $label = $gladexml->get_widget('ipsecbuttonlabel');
	my $image = $gladexml->get_widget('ipsecbuttonstatusimage');
	$image->set_from_icon_name('gtk-disconnect','button');
	$label->set_text('Stop VPNs');
	my $statusimage = $gladexml->get_widget('ipsecstatusimage');
	$statusimage->set_from_icon_name('gtk-yes','button');
	
}

sub ipsecbutton_off {
	my $label = $gladexml->get_widget('ipsecbuttonlabel');
	my $image = $gladexml->get_widget('ipsecbuttonstatusimage');
	$image->set_from_icon_name('gtk-connect', 'button');
	$label->set_text('Start VPNs');
	my $statusimage = $gladexml->get_widget('ipsecstatusimage');
	$statusimage->set_from_icon_name('gtk-no','button');
}

# return of 0 = success, otherwise 
sub start_racoon {
	my $ret;
	# these report errors on their own
	return 1 if ! check_config_dir_permissions();
	$myip = get_ip(get_main_interface());
	return 1 if ! defined $myip;
	return 1 if ! generate_racoon_and_setkey_config();
	return 1 if ! generate_psk_file();
	# these need to have their errrors reported
	$ret = `$helper_path setperms`;
	return "Unable to set permissions on config files.($?)" if $?;
	my $startcommand = "$helper_path start ".($configuration{'logdebug'} == 1 ? 'debug' : '' );
	$ret = `$startcommand`;
	return "Unable to start racoon.($?)" if $?;
	$ret = `$helper_path setspd`;
	return "Unable to set SPDs.($?)" if $?;  
	prepare_vpn_status_info();

	Glib::Timeout->add( $policy_check_frequency , \&check_vpn_states) if $configuration{'monitorvpns'};

	# start any vpns that have 'ikenow' set
	foreach my $vpn ( keys %vpns ){
		next if( ! $vpns{$vpn}->{'ikenow'} or ! $vpns{$vpn}->{'enabled'});
		# this is not optimal, but it seems to work
		my $ip = $vpns{$vpn}->{'remote'} ne '' ? $vpns{$vpn}->{'remote'} : $vpns{$vpn}->{'gateway'};
		$ip =~ m#^([0-9.]+)(/\d+)?#;
		system "/bin/ping -c1 $1 &> /dev/null";
	}
	return 0;
}

# return of 0 = success, otherwise
sub stop_racoon {
	my $ret;
	$ret = `$helper_path stop`;
	return "Unable to stop racoon IKE daemon.($?)" if $?;
	$ret = `$helper_path flushspd`;
	return "Unable to flush SPD database.($?)" if $?;
	$ret = `$helper_path cleanup`;
	return "Unable to remove temporary configuration files.($?)" if $?;
	check_vpn_states();
	return 0;
}

# get PID of current racoon process and verify that it is valid,
# if not, remove it.
sub get_racoon_pid {
	my $pidfile = $config_dir."racoon.pid";
	if( -e $pidfile ){
		#pid file exists, but can't open it
		show_error_dialog( "Unable to open racoon PID file.\n", 1) if( ! open PIDFILE, $config_dir."racoon.pid" );
		my $pid = <PIDFILE>;
		close PIDFILE;
		#pid does not exist
		if( ! -f "/proc/$pid/status" ){
			print "Stale racoon PID file, PID=$pid\n" if $debug;
			unlink $pidfile;
			return 0;
		}
		if (! open RACOONPROCINFO, "/proc/$pid/status" ){
			show_error_dialog( "Can't read PID info from: /proc/$pid/status. Not sure what to do.",1);
		}
		my $status = <RACOONPROCINFO>;
		close RACOONPROCINFO;
		$status =~ /^Name:\s+(\S+)$/;
		# process exists, but is not racoon
		if( $1 ne 'racoon'){
			print "Huh? PID is not that of a racoon process\n"  if $debug;
			unlink $pidfile;
			return 0;
		}
		#pid is ok
		return $pid
	}else{
		return 0;	
	}
}

########################################################################
# VPN status checks
########################################################################


# called via a Glib::timeout periodically to:
#    check if racoon is running.
#    check that starting was successfull     
#    check that stopping was successfull
#    change start/stop button state if needed.
sub check_racoon_status {
	return 1 if $ipsec_state eq 'stopped';# nothing to do in this state
	
	$racoon_pid = get_racoon_pid();
	
	# racoon must have stopped, cleanup after it and update the interface
	if( $racoon_pid == 0 and $ipsec_state eq 'started' ){
		system ( $helper_path, "cleanup" );
		system ( $helper_path, "flushspd" );
		$ipsec_state = 'stopped';
	}
	
	#check_vpn_states() if $ipsec_state eq 'started' ;
	
	return 1 if( ! ($ipsec_state =~ /^starting|stopping$/ ) );

	$state_checks++; #count how many times we checked if starting/stopping was successfull
	if( $ipsec_state eq 'starting' ){
		if( $racoon_pid != 0 ){
			ipsecbutton_on();
			$ipsec_state = 'started';
			$state_checks=0;
			$gladexml->get_widget('vpnaddbutton')->set_sensitive(0);
			$gladexml->get_widget('vpneditbutton')->set_sensitive(0);
			$gladexml->get_widget('vpnremovebutton')->set_sensitive(0);
			$gladexml->get_widget('configtreeview')->set_sensitive(0);
		}
		if( $state_checks > 3 ){
			show_error_dialog( "Problems starting the IKE daemon.", 0);
			$ipsec_state='stopped';
			system("$helper_path", "cleanup");
			show_error_dialog( "Unable to remove temporary configuration files.($?)") if $?;
		}
	}elsif( $ipsec_state eq 'stopping' ){
		if( $racoon_pid == 0  ){
			ipsecbutton_off();
			$ipsec_state = 'stopped';
			$racoon_pid = 0;
			$state_checks=0;
			exit if ( $quit_after_stop );
			$gladexml->get_widget('vpnaddbutton')->set_sensitive(1);
			$gladexml->get_widget('vpnremovebutton')->set_sensitive(1);
			$gladexml->get_widget('vpneditbutton')->set_sensitive(1);
			$gladexml->get_widget('configtreeview')->set_sensitive(1);
		}
		if( $state_checks > 3 ){
			show_error_dialog( "Problems stopping the IKE daemon.", 0);
			exit if ( $quit_after_stop );
			$ipsec_state='started';
		}
	}
	
	if( $ipsec_state =~ /^started|stopped$/ ){
		$gladexml->get_widget('ipsecbutton')->set_sensitive(1);
		$gladexml->get_widget('ipsecbutton')->enter();	
	}
	return 1;
}



sub check_vpn_states {
	return 0 if $ipsec_state eq 'stopped';#stop Glib::Timeout from calling this
	my $model = $gladexml->get_widget('configtreeview')->get_model;
	my $iter = $model->get_iter_first ;
	do{
		my $name = $model->get( $iter, VPNLIST_NAME );
		if( $vpns{$name}->{'enabled'} ){
			my $state = system($helper_path,'checkpol', @{$vpninfo{$name}->{'polinfo'}} ) ? 'Off' : 'On';
			$model->set ($iter, VPNLIST_STATUS, $state );
		}
	}while( $iter = $model->iter_next( $iter ) )
}


#used to gather info from the vpn configs that is used to check the vpn status.
sub prepare_vpn_status_info {
	%vpninfo = ();
	foreach my $vpn ( keys %vpns ){
		if( $vpns{$vpn}->{'mode'} eq 'host-host-transport' ){
			$vpninfo{$vpn}->{'polinfo'} = [ $myip, $vpns{$vpn}->{'gateway'},$myip,$vpns{$vpn}->{'gateway'}];
		}elsif( $vpns{$vpn}->{'mode'} eq 'host-net' ){
			my $localip = $vpns{$vpn}->{'local'} ne '' ? $vpns{$vpn}->{'local'} : $myip;
			$vpninfo{$vpn}->{'polinfo'} = [$localip,$vpns{$vpn}->{'gateway'},$localip,$vpns{$vpn}->{'remote'}];
		}elsif( $vpns{$vpn}->{'mode'} eq 'host-host-tunnel'){
			my $localip = $vpns{$vpn}->{'local'} ne '' ? $vpns{$vpn}->{'local'} : $myip;
			my $remoteip = $vpns{$vpn}->{'remote'} ne '' ? $vpns{$vpn}->{'remote'} : $vpns{$vpn}->{'gateway'};
			$vpninfo{$vpn}->{'polinfo'} = [$localip,$vpns{$vpn}->{'gateway'},$localip,$remoteip];
		}elsif( $vpns{$vpn}->{'mode'} eq 'net-net' ){
			$vpninfo{$vpn}->{'polinfo'} = [$myip, $vpns{$vpn}->{'gateway'}, $vpns{$vpn}->{'local'},$vpns{$vpn}->{'remote'}];
		}elsif( $vpns{$vpn}->{'mode'} eq 'host-any' ){
			$vpninfo{$vpn}->{'polinfo'} = [$myip,$vpns{$vpn}->{'gateway'},$myip,"0.0.0.0/0"];
		}
	}
}

########################################################################
# Racoon config creation
########################################################################

sub generate_psk_file {
	if( ! open PSK, "+>$racoon_psk_file" ){
		show_error_dialog( "Unable to open psk file '$racoon_psk_file' for writing: $!" , 0 );
		return;
	}
	foreach my $vpn ( keys %vpns ){
		next if( ! $vpns{$vpn}->{'enabled'});
		print PSK $vpns{$vpn}->{'gateway'}." ".$vpns{$vpn}->{'psk'}."\n" if $vpns{$vpn}->{'psk'};
	}
	close PSK;
	return 1;
}

sub generate_racoon_and_setkey_config {
	
	if( ! open SETKEY, "+>$setkey_config" ){
		show_error_dialog( "Unable to open setkey config file '$setkey_config' for writing: $!" , 0 );
		return;
	}
	print SETKEY "flush;\nspdflush;\n";
	
	if( ! open RC, "+>$racoon_config_file" ){
		show_error_dialog( "Unable to open racoon config file '$racoon_config_file' for writing: $!" , 0 );
		return;
	}
	print RC "path pre_shared_key \"$racoon_psk_file\";\n\n";
	print RC "log notify;\n";
	print RC <<PADDINGTIMER;
padding
{
	maximum_length 20;	# maximum padding length.
	randomize off;		# enable randomize length.
	strict_check off;	# enable strict check.
	exclusive_tail off;	# extract last one octet.
}

timer
{
	# These value can be changed per remote node.
	counter 5;		# maximum trying count to send.
	interval 20 sec;	# maximum interval to resend.
	persend 1;		# the number of packets per a send.

	# timer for waiting to complete each phase.
	phase1 30 sec;
	phase2 15 sec;
}
PADDINGTIMER

	foreach my $vpn ( keys %vpns ){
		next if ! $vpns{$vpn}->{'enabled'};

		my $localid;
		my $localidtype = 'udn' if $vpns{$vpn}->{'localidtype'} =~ /\@/;
		$localidtype = mapping_get_long('id_types', $vpns{$vpn}->{'localidtype'});
		if( $vpns{$vpn}->{'localidtype'} eq 'dn' ){
			$localid = $vpns{$vpn}->{'localiddn'}
		}elsif( $vpns{$vpn}->{'localidtype'} eq 'addr' ){
			$localid = '';
		}
		my $remoteid;
		my $remoteidtype = 'udn' if $vpns{$vpn}->{'remoteidtype'} =~ /\@/;
		$remoteidtype = mapping_get_long('id_types', $vpns{$vpn}->{'remoteidtype'});
		if( $vpns{$vpn}->{'remoteidtype'} eq 'dn' ){
			$remoteid = $vpns{$vpn}->{'remoteiddn'}
		}elsif( $vpns{$vpn}->{'remoteidtype'} eq 'addr' ){
			$remoteid = '';#'"'.$vpns{$vpn}->{'gateway'}.'"';
		}

# TODO no other auth methods yet...
		my $authmethod;
#		if( $vpns{$vpn}->{''}  eq  ){
			$authmethod = 'pre_shared_key';
#		}
		$vpns{$vpn}->{'p1dhgroup'} =~ /dh(\d+)/;
		my $p1dh = $1;
		
		my $said;
		
		if( $vpns{$vpn}->{'mode'} eq 'host-host-transport' ){
			$said = "address $myip any address ".$vpns{$vpn}->{'gateway'}." any";
			print SETKEY "spdadd $myip/32 ".$vpns{$vpn}->{'gateway'}."/32 any -P out ipsec esp/transport//require ah/transport//require";
			print SETKEY "spdadd ".$vpns{$vpn}->{'gateway'}."/32 $myip/32 any -P in ipsec esp/transport//require ah/transport//require";
		}elsif( $vpns{$vpn}->{'mode'} eq 'host-host-tunnel' ){
			my $localip = $vpns{$vpn}->{'local'} ne '' ? $vpns{$vpn}->{'local'} : $myip;
			my $remoteip = $vpns{$vpn}->{'remote'} ne '' ? $vpns{$vpn}->{'remote'} : $vpns{$vpn}->{'gateway'};
			$said = "address $localip any address $remoteip any";
			print SETKEY "spdadd $remoteip $localip any -P in ipsec esp/tunnel/".$vpns{$vpn}->{'gateway'}."-$localip/unique;\n";
			print SETKEY "spdadd $localip $remoteip any -P out ipsec esp/tunnel/$localip-".$vpns{$vpn}->{'gateway'}."/unique;\n\n";
		}elsif( $vpns{$vpn}->{'mode'} eq 'host-net' ){
			my $localip = $vpns{$vpn}->{'local'} ne '' ? $vpns{$vpn}->{'local'} : $myip;
			$said = "address $localip any address ".$vpns{$vpn}->{'remote'}." any";
			print SETKEY "spdadd ".$vpns{$vpn}->{'remote'}." $localip/32 any -P in ipsec esp/tunnel/".$vpns{$vpn}->{'gateway'}."-".$localip."/unique;\n";
			print SETKEY "spdadd $localip/32 ".$vpns{$vpn}->{'remote'}." any -P out ipsec esp/tunnel/$localip-".$vpns{$vpn}->{'gateway'}."/unique;\n\n";
		}elsif( $vpns{$vpn}->{'mode'} eq 'net-net' ){
			$said = "address ".$vpns{$vpn}->{'local'}." any address ".$vpns{$vpn}->{'remote'}." any";
			print SETKEY "spdadd ".$vpns{$vpn}->{'remote'}." ".$vpns{$vpn}->{'local'}." any -P in ipsec esp/tunnel/".$vpns{$vpn}->{'gateway'}."-$myip/unique;\n";
			print SETKEY "spdadd ".$vpns{$vpn}->{'local'}." ".$vpns{$vpn}->{'remote'}." any -P out ipsec esp/tunnel/$myip-".$vpns{$vpn}->{'gateway'}."/unique;\n\n";
		}elsif( $vpns{$vpn}->{'mode'} eq 'host-any' ){
			$said = "address $myip any address 0.0.0.0/0 any";
			if( $vpns{$vpn}->{'dhcppt'} ){
				print SETKEY "spdadd ".$myip."[68] 0.0.0.0/0[67] any -P none;";
				print SETKEY "spdadd 0.0.0.0/0[67] ".$myip."[68] any -P none;";
			}	
			print SETKEY "spdadd $myip/32 0.0.0.0/0 any -P out ipsec esp/tunnel/$myip-".$vpns{$vpn}->{'gateway'}."/require;";
			print SETKEY "spdadd 0.0.0.0/0 $myip/32 any -P in ipsec esp/tunnel/".$vpns{$vpn}->{'gateway'}."-$myip/require;";
		}

		$vpns{$vpn}->{'p2pfsgroup'} =~ /dh(\d+)/;
		my $p2pfs = $1;
		
		my($compression,$sit,$doi,$vrfyid,$initial,$passive,$genpol,$vrfycert,$supportproxy);
		$compression = $vpns{$vpn}->{'deflate'} ? "compression_algorithm deflate;" : '';
		$sit = $vpns{$vpn}->{'sit'} ? "situation identity_only;" : '' ;
		$doi = $vpns{$vpn}->{'doi'} ? "doi ipsec_doi;" : '';
		$vrfyid = $vpns{$vpn}->{'vrfyid'} ? "verify_identifier on;" : '';
		$initial = $vpns{$vpn}->{'initial'} ? "initial_contact on;" : '';
		$passive = $vpns{$vpn}->{'passive'} ? "passive on;" : '';
		$genpol = $vpns{$vpn}->{'genpol'} ? "generate_policy on;" : '';
		$vrfycert = "verify_cert ".( $vpns{$vpn}->{'vrfycert'} ? 'on':'off').";";
		$supportproxy = $vpns{$vpn}->{'supportproxy'} ? "support_proxy on;" : '';
		
		my $p2enc = join ',', @{$vpns{$vpn}->{'p2encryption'}};
		my $p2auth = join ',', @{$vpns{$vpn}->{'p2authentication'}};
		
		print RC <<REMOTE;
remote $vpns{$vpn}->{'gateway'}
{
	exchange_mode $vpns{$vpn}->{'exchange_mode'};
	$doi
	$sit
	$vrfyid
	$supportproxy
	nat_traversal on;
	my_identifier $localidtype "$localid";
	peers_identifier $remoteidtype $remoteid;
	nonce_size $vpns{$vpn}->{'nonce'};
	lifetime time $vpns{$vpn}->{'p1lifetime'} sec;
	$initial
	proposal_check $vpns{$vpn}->{'proposal_check'};
	proposal {
		encryption_algorithm $vpns{$vpn}->{'p1encryption'};
		hash_algorithm $vpns{$vpn}->{'p1authentication'};
		authentication_method $authmethod;
		dh_group $p1dh;
	}
}

sainfo $said
{
	pfs_group $p2pfs;
	lifetime time $vpns{$vpn}->{'p2lifetime'} sec;
	encryption_algorithm $p2enc;
	authentication_algorithm $p2auth;
	$compression
}

REMOTE

	}
    close RC;


}

