# Sample SpecTcl main program for testing GUI

package VPS::keyManager;

use strict;
use VPS;
use VPS::simpleAlert;

use Tk;
use Tk::Menu;
use Ssh::TkTunnel;

use Exporter;

@VPS::keyManager::ISA = qw(Exporter);
@VPS::keyManager::EXPORT = qw(key_ui);

# interface generated by SpecTcl (Perl enabled) version 1.1 
# from tunnelBuilder.ui
# For use with Tk402.002, using the grid geometry manager

local $VPS::keyManager::root;
local $VPS::keyManager::entKeyDirectory;

my($lbTunnelKey,$butNewKey,$butRevokeKey);
sub key_ui {
    my $refData = $VPS::globalrefdata;

    if ((windowOpen('key_ui')) || (windowOpen('tunnelBuilder')) || (windowOpen('route_ui')) || (windowOpen('simpleAlert'))) {
	message("Function currently inaccessable.");
	return 0;
    }
    return 0 if (VPS::openWindow("key_ui"));

    $VPS::keyManager::root = MainWindow->new();
    $VPS::keyManager::root->title("VPS $main::VPSversion Administrator - Tunnel Manager");
    # widget creation 
    
    my($frame_1) = $VPS::keyManager::root->Frame (
				 );
    my($frame_2) = $VPS::keyManager::root->Frame (
				 );
    my($frame_3) = $VPS::keyManager::root->Frame (
				 );
    my($frame_4) = $VPS::keyManager::root->Frame (
				 );
    my($frame_5) = $VPS::keyManager::root->Frame (
				 );
    my($label_1) = $VPS::keyManager::root->Label (
				 -text => 'Key Directory',
				 );
    $VPS::keyManager::entKeyDirectory = $VPS::keyManager::root->Entry (				     
				     );
    $VPS::keyManager::entKeyDirectory->insert(0,$VPS::keydir);
    my($label_2) = $VPS::keyManager::root->Label (
				 -anchor => 'sw',
				 -justify => 'left',
				 -font => "-*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*",
				 -text => buildKeyLabelString("Tunnel Name","","Key File"),
				 );
    ($lbTunnelKey) = $VPS::keyManager::root->Scrolled ('Listbox',
				      -scrollbars => 'e',
				      -font => "-*-Courier-Medium-R-Normal--*-120-*-*-*-*-*-*",
				      -height => '5',
				      -width => '0',
				      );
    $lbTunnelKey->bind('<Double-1>' => [\&manageKey,$refData,$lbTunnelKey]);
    if ($refData) {
	my $numtunnels = $#$refData;
	my $i;
	for ($i=0;$i<=$numtunnels;$i++) {
	    my $string = buildKeyLabelString($$refData[$i]->{'tunnelName'},$VPS::keydir,$$refData[$i]->{'keyFile'});
	    $lbTunnelKey->insert($i,$string);
	}
    }
    my($butNewKey) = $VPS::keyManager::root->Button (
				    -text => 'New Key...',
				    -command => [\&newKey,$refData,$lbTunnelKey]
				    );
    my($butModifyKey) = $VPS::keyManager::root->Button (
				    -text => 'Change Keyfile...',
				    -command => [\&modifyKey,$refData,$lbTunnelKey]
				    );
#    my($butRevokeKey) = $VPS::keyManager::root->Button (
#				       -text => 'Revoke Key...',
#				       -command => [\&revokeKey,$refData,$lbTunnelKey]
#				       );
    
    my($butCheckOrAddRoutes) = $VPS::keyManager::root->Button (
							       -text => 'Check or Add Routes',
							       -command => [\&checkOrAddRoutes,$refData,$lbTunnelKey]
				       );
    
    my($butStartTunnel) = $VPS::keyManager::root->Button (
							  -text => 'Start Tunnel',
							  -command => [\&startTunnel,$refData,$lbTunnelKey]
				       );

    my($butStopTunnel) = $VPS::keyManager::root->Button (
							 -text => 'Stop Tunnel',
							 -command => [\&stopTunnel,$refData,$lbTunnelKey]
				       );
    my($butKeyOK) = $VPS::keyManager::root->Button (
				   -text => 'OK',
				   -command => \&updateKeyCommand
					  );
    $VPS::keyManager::root->protocol("WM_DELETE_WINDOW" => \&updateKeyCommand);
    
    # Geometry management
    
    $frame_1->grid(
		   -in => $VPS::keyManager::root,
		   -column => '1',
		   -row => '1',
		   -sticky => 'nesw',
		   -padx => 5,
		   -pady => 5,
		   );
    $frame_2->grid(
		   -in => $VPS::keyManager::root,
		   -column => '1',
		   -row => '2',
		   -sticky => 'nesw'
		   );
    $frame_3->grid(
		   -in => $VPS::keyManager::root,
		   -column => '1',
		   -row => '3',
		   -sticky => 'nesw',
		   -pady => 10
		   );
    $frame_4->grid(
		   -in => $VPS::keyManager::root,
		   -column => '1',
		   -row => '5',
		   -sticky => 'nesw'
		   );
    $frame_5->grid(
		   -in => $VPS::keyManager::root,
		   -column => '1',
		   -row => '4',
		   -sticky => 'nesw'
		   );
    $label_1->grid(
		   -in => $frame_1,
		   -column => '1',
		   -row => '1',
		   -sticky => 'sw'
		   );
    $VPS::keyManager::entKeyDirectory->grid(
			   -in => $frame_1,
			   -column => '2',
			   -row => '1',
			   -sticky => 'sw'
			   );
    $label_2->grid(
		   -in => $frame_2,
		   -column => '1',
		   -row => '1',
		   -sticky => 'sw'
		   );
    $lbTunnelKey->grid(
		       -in => $frame_2,
		       -column => '1',
		       -row => '2',
		       -sticky => 'nesw'
		       );
    $butNewKey->grid(
		     -in => $frame_3,
		     -column => '1',
		     -row => '1',
		     -sticky => 'n'
		     );
    $butModifyKey->grid(
		     -in => $frame_3,
		     -column => '2',
		     -row => '1',
		     -sticky => 'n'
		     );
#    $butRevokeKey->grid(
#			-in => $frame_3,
#			-column => '3',
#			-row => '1',
#			-sticky => 'n'
#			);
    $butCheckOrAddRoutes->grid(
			      -in => $frame_5,
			      -column => '1',
			      -row => '1',
			      -sticky => 'n',
			      -pady => 20
			      );
    $butStartTunnel->grid(
			  -in => $frame_5,
			  -column => '2',
			  -row => '1',
			  -sticky => 'n',
			  -pady => 20
			  );
    $butStopTunnel->grid(
			 -in => $frame_5,
			 -column => '3',
			 -row => '1',
			 -sticky => 'n',
			 -pady => 20
			 );
    $butKeyOK->grid(
		       -in => $frame_4,
		       -column => '1',
		       -row => '1',
		       );
    # Resize behavior management
    
    # container $frame_1 (rows)
    $frame_1->gridRowconfigure(1, -weight  => 0, -minsize  => 30);
    
    # container $frame_1 (columns)
    $frame_1->gridColumnconfigure(1, -weight => 0, -minsize => 30);
    $frame_1->gridColumnconfigure(2, -weight  => 0, -minsize  => 30);
    
    # container $frame_1 (rows)
    $frame_2->gridRowconfigure(1, -weight  => 0, -minsize  => 30);
    $frame_2->gridRowconfigure(2, -weight  => 1, -minsize  => 30);
    
    # container $frame_1 (columns)
    $frame_2->gridColumnconfigure(1, -weight => 1, -minsize => 30);
    
    # container $frame_2 (rows)
    $frame_3->gridRowconfigure(1, -weight  => 0, -minsize  => 30);
    
    # container $frame_2 (columns)
    $frame_3->gridColumnconfigure(1, -weight => 0, -minsize => 30);
    $frame_3->gridColumnconfigure(2, -weight => 0, -minsize => 30);
    $frame_3->gridColumnconfigure(3, -weight => 0, -minsize => 30);
    
    # container $frame_2 (rows)
    $frame_5->gridRowconfigure(1, -weight  => 0, -minsize  => 30);
    
    # container $frame_2 (columns)
    $frame_5->gridColumnconfigure(1, -weight => 0, -minsize => 30);
    $frame_5->gridColumnconfigure(2, -weight => 0, -minsize => 30);
    $frame_5->gridColumnconfigure(3, -weight => 0, -minsize => 30);
    
    # container $frame_3 (rows)
    $frame_4->gridRowconfigure(1, -weight  => 0, -minsize  => 30);
    
    # container $frame_3 (columns)
    $frame_4->gridColumnconfigure(1, -weight  => 0, -minsize  => 30);
    
    # container $VPS::keyManager::root (rows)
    $VPS::keyManager::root->gridRowconfigure(1, -weight  => 0, -minsize  => 30);
    $VPS::keyManager::root->gridRowconfigure(2, -weight  => 1, -minsize  => 30);
    $VPS::keyManager::root->gridRowconfigure(3, -weight  => 0, -minsize  => 30);
    $VPS::keyManager::root->gridRowconfigure(4, -weight  => 0, -minsize  => 30);
    $VPS::keyManager::root->gridRowconfigure(5, -weight  => 0, -minsize  => 30);
    
    # container $VPS::keyManager::root (columns)
    $VPS::keyManager::root->gridColumnconfigure(1, -weight => 1, -minsize => 30);
    
    # additional interface code
    
    # end additional interface code
}

sub updateKeyCommand {

    my $string = $VPS::keyManager::entKeyDirectory->get();
    if ($string ne $VPS::keydir) {
	if (simpleAlert($VPS::mainWindow::root,"Change the Key Directory to $string?")) {
	    $VPS::keydir = $string;
	}
    }
  VPS::closeWindow("key_ui");
    $VPS::keyManager::root->destroy;
}

sub newKey {
    my ($refData,$lbref) = @_;

    if ((windowOpen("simpleAlert") || (windowOpen("input_dialog")) || (windowOpen("information_alert")))) {
	message("Function currently inaccessable.");
	return 0;
    }
    my $index = $lbref->curselection if (defined($lbref));
    if (length($index) == 0) {
      VPS::simpleAlert::simpleAlert($VPS::mainWindow::root,"No Tunnel Selected.");
	return 1;
    }
    my $string = $lbref->get($index,$index);
    my ($tunnelName,$keyFile) = split(' ',$string);
    if(!simpleAlert($VPS::mainWindow::root,"Create new key for $tunnelName?")) {
	return -1;
    }
    my ($err,$ret) = input_dialog($VPS::mainWindow::root,"Key File Name:");
    if ($err != 1) {
	information_alert($VPS::mainWindow::root,"You must input a key file name.");
	return -1;
    }
    if ($ret !~ /^[\w|-]+$/) {
	information_alert($VPS::mainWindow::root,"Key File names must be made up of only letters, numbers underscores and dashes.");
	return -1;
    }
    $keyFile = $ret;
    my $tunnel;
    if ($refData->[$index]{'tunnelObject'}) {
	$tunnel = $refData->[$index]{'tunnelObject'};
    }
    else {
	$refData->[$index]{'tunnelObject'} = 
	    new Ssh::TkTunnel($VPS::mainWindow::root,
			      -name => $refData->[$index]{'tunnelName'},
			      -keyfile => $keyFile,
			      -pubkeyfile => "$keyFile.pub",
			      -local => $refData->[$index]{'tunnelLocalAddr'},
			      -remote => $refData->[$index]{'tunnelRemoteAddr'},
			      -gateway => $refData->[$index]{'tunnelGatewayAddr'},
			      -localroute => $refData->[$index]{'tunnelLocalRoute'},
			      -remoteuser => "root",
			      -remoteroute => $refData->[$index]{'tunnelRemoteRoute'});
	$tunnel = $refData->[$index]{'tunnelObject'};
    }
    if ($VPS::keydir) {
	if (!(-d $VPS::keydir)) { # If the Key Directory doesn't exist
	    if (simpleAlert($VPS::mainWindow::root,"Key Directory \"$VPS::keydir\" does not exist.  Create it?")) {
		if (!(mkdir($VPS::keydir,0700))) {
		    information_alert($VPS::mainWindow::root,"Could not create directory \"$VPS::keydir\".  Please make sure that the parent directories exist.");
		    return -1;
		}
	    }
	    else {
		return -1;
	    }
	}
	($err,$ret) = $tunnel->check_or_generate_tunnel_key($keyFile,$VPS::keydir,$refData->[$index]{'tunnelName'});
	if ($ret) {
	    information_alert($VPS::mainWindow::root,$ret);
	    return -1;
	}
    }
    else {
	information_alert($VPS::mainWindow::root,"The Key Directory is not set.  Please set it first.");
	return -1;
    }
    $refData->[$index]{'keyFile'} = $keyFile;
    $string = buildKeyLabelString($$refData[$index]->{'tunnelName'},$VPS::keydir,$$refData[$index]->{'keyFile'});
    $lbTunnelKey->delete($index);
    $lbTunnelKey->insert($index,$string);
    information_alert($VPS::mainWindow::root,"New Key File $VPS::keydir/$keyFile created for $refData->[$index]{'tunnelName'} tunnel.");
    return 0;
}

sub revokeKey {
    my ($refData,$lbref) = @_;

    if ((windowOpen("simpleAlert") || (windowOpen("input_dialog")) || (windowOpen("information_alert")))) {
	message("Function currently inaccessable.");
	return 0;
    }
    my $index = $lbref->curselection if (defined($lbref));
    if (length($index) == 0) {
      VPS::simpleAlert::simpleAlert($VPS::mainWindow::root,"No Key Selected.");
	return 1;
    }
    my $string = $lbref->get($index,$index);
    my ($tunnelName,$keyFile) = split(' ',$string);
    my $keyfile = $refData->[$index]{'keyFile'};
    if(!simpleAlert($VPS::mainWindow::root,"Revoke key \"$keyfile\" for $tunnelName?")) {
	return -1;
    }
    if(!simpleAlert($VPS::mainWindow::root,"This will revoke a tunnel key, and cannot be undone!  Are you sure you want to do this?")) {
	return -1;
    }
    # I really need to delete the remote end's authorized_keys entry, 
    # but I'll do that later...
    unlink($keyFile);
    $$refData[$index]->{'keyFile'} = "";
    $string = buildKeyLabelString($$refData[$index]->{'tunnelName'},$VPS::keydir,$$refData[$index]->{'keyFile'});
    $lbTunnelKey->delete($index);
    $lbTunnelKey->insert($index,$string);
  information_alert($VPS::mainWindow::root,"Key Revoked.");   
}

sub modifyKey {
    my ($refData,$lbref) = @_;

    if ((windowOpen("simpleAlert") || (windowOpen("input_dialog")) || (windowOpen("information_alert")))) {
	message("Function currently inaccessable.");
	return 0;
    }
    my $index = $lbref->curselection if (defined($lbref));
    if (length($index) == 0) {
      VPS::simpleAlert::simpleAlert($VPS::mainWindow::root,"No Key Selected.");
	return 1;
    }
    my $string = $lbref->get($index,$index);
    my ($tunnelName,$keyFile) = split(' ',$string);
    my $keyfile = $refData->[$index]{'keyFile'};
    if(!simpleAlert($VPS::mainWindow::root,"Change key file for $tunnelName?")) {
	return -1;
    }
    my ($err,$ret) = input_dialog($VPS::mainWindow::root,"New Keyfile:");
    if ($err != 1) {
	information_alert($VPS::mainWindow::root,"You must input a key file name.");
	return -1;
    }
    if ($ret !~ /^[\w|-]+$/) {
	information_alert($VPS::mainWindow::root,"Key File names must be made up of only letters, numbers underscores and dashes.");
	return -1;
    }
    $keyFile = $ret;
    # I really need to delete the remote end's authorized_keys entry, 
    # but I'll do that later...
    $$refData[$index]->{'keyFile'} = $keyFile;
    $string = buildKeyLabelString($$refData[$index]->{'tunnelName'},$VPS::keydir,$$refData[$index]->{'keyFile'});
    $lbTunnelKey->delete($index);
    $lbTunnelKey->insert($index,$string);
  information_alert($VPS::mainWindow::root,"Key File Changed.");   
}

sub checkOrAddRoutes {
    my ($refData,$lbref) = @_;

    if ((windowOpen("simpleAlert") || (windowOpen("input_dialog")) || (windowOpen("information_alert")))) {
	message("Function currently inaccessable.");
	return 0;
    }
    my $index = $lbref->curselection if (defined($lbref));
    if (length($index) == 0) {
      VPS::simpleAlert::simpleAlert($VPS::mainWindow::root,"No Tunnel Selected.");
	return 1;
    }
    my $string = $lbref->get($index,$index);
    my ($tunnelName,$keyFile) = split(' ',$string);
    information_alert($VPS::mainWindow::root,"Checking local and remote routes, and adding routes if necessary...");
    my $tunnel;
    if ($refData->[$index]{'tunnelObject'}) {
	$tunnel = $refData->[$index]{'tunnelObject'};
    }
    else {
	$refData->[$index]{'tunnelObject'} = 
	    new Ssh::TkTunnel($VPS::mainWindow::root,
			      -name => $refData->[$index]{'tunnelName'},
			      -keyfile => $keyFile,
			      -pubkeyfile => "$keyFile.pub",
			      -local => $refData->[$index]{'tunnelLocalAddr'},
			      -remote => $refData->[$index]{'tunnelRemoteAddr'},
			      -gateway => $refData->[$index]{'tunnelGatewayAddr'},
			      -localroute => $refData->[$index]{'tunnelLocalRoute'},
			      -remoteuser => "root",
			      -remoteroute => $refData->[$index]{'tunnelRemoteRoute'});
	$tunnel = $refData->[$index]{'tunnelObject'};
    }
    $tunnel->verbose(1);
    my ($err,$ret) = $tunnel->check_or_add_key_to_authorized_keys();
    if ($err && $ret) {
	information_alert($VPS::mainWindow::root,$ret);
	return(-1,$ret);
    }
    ($err,$ret) = $tunnel->check_or_add_tunnel_routes();
    if ($err && $ret) {
	information_alert($VPS::mainWindow::root,$ret);
	return(-1,$ret);
    }
    $tunnel->verbose(0);
}

sub startTunnel {
    my ($refData,$lbref) = @_;

    if ((windowOpen("simpleAlert") || (windowOpen("input_dialog")) || (windowOpen("information_alert")))) {
	message("Function currently inaccessable.");
	return 0;
    }
    my $index = $lbref->curselection if (defined($lbref));
    if (length($index) == 0) {
      VPS::simpleAlert::simpleAlert($VPS::mainWindow::root,"No Tunnel Selected.");
	return 1;
    }
    my $string = $lbref->get($index,$index);
    my ($tunnelName,$keyFile) = split(' ',$string);
    information_alert($VPS::mainWindow::root,"Bringing up $tunnelName tunnel...");
    my $tunnel;
    if ($refData->[$index]{'tunnelObject'}) {
	$tunnel = $refData->[$index]{'tunnelObject'};
    }
    else {
	$refData->[$index]{'tunnelObject'} = 
	    new Ssh::TkTunnel($VPS::mainWindow::root,
			      -name => $refData->[$index]{'tunnelName'},
			      -keyfile => $keyFile,
			      -pubkeyfile => "$keyFile.pub",
			      -local => $refData->[$index]{'tunnelLocalAddr'},
			      -remote => $refData->[$index]{'tunnelRemoteAddr'},
			      -gateway => $refData->[$index]{'tunnelGatewayAddr'},
			      -localroute => $refData->[$index]{'tunnelLocalRoute'},
			      -remoteuser => "root",
			      -remoteroute => $refData->[$index]{'tunnelRemoteRoute'});
	$tunnel = $refData->[$index]{'tunnelObject'};
    }
    $tunnel->verbose(1);
    my ($err,$ret) = $tunnel->bring_up_tunnel();
    if ($err < 0) {
	information_alert($VPS::mainWindow::root,$ret) if ($ret); 
    }
    $tunnel->verbose(0);
}

sub stopTunnel {
    my ($refData,$lbref) = @_;

    if ((windowOpen("simpleAlert") || (windowOpen("input_dialog")) || (windowOpen("information_alert")))) {
	message("Function currently inaccessable.");
	return 0;
    }
    my $index = $lbref->curselection if (defined($lbref));
    if (length($index) == 0) {
      VPS::simpleAlert::simpleAlert($VPS::mainWindow::root,"No Tunnel Selected.");
	return 1;
    }
    my $string = $lbref->get($index,$index);
    my ($tunnelName,$keyFile) = split(' ',$string);
    my $tunnel;
    if (!($refData->[$index]{'tunnelName'})) {
	information_alert($VPS::mainWindow::root,"No Tunnel Selected.");
	return 1;
    }
    information_alert($VPS::mainWindow::root,"Stopping $tunnelName tunnel...");
    if (!open(PS,"ps -axwww|")) {
	information_alert($VPS::mainWindow::root,"Wierd - Could not do a ps, could you be running out of memory?  Aborting...");
	return 1;
    }
    $string = $refData->[$index]{'tunnelLocalAddr'} . ':' . $refData->[$index]{'tunnelRemoteAddr'};
    while (<PS>) {
	chop;
	if (/$string/) {
	    if (/^(\S+)/) {
		my $pid = $1;
		kill ('HUP', $pid);
		information_alert($VPS::mainWindow::root,"Sent HUP signal to $tunnelName tunnel, pid $pid");
		close (PS);
		return 0;
	    }
	    else {
		information_alert($VPS::mainWindow::root,"Bug found!  Please report the following attributes to vps-bugs\@strongcrypto.com: \"ps: $string\"");
		close (PS);
		return 1;
	    }
	}
    }
    information_alert($VPS::mainWindow::root,"Could not find $tunnelName tunnel process.  Are you sure it is running?");
    close(PS);
    return 1;
}

1;
