#!/usr/bin/perl -w

# Author: Bubli <kmachalkova@suse.cz>
#
# An agent for parsing /etc/sudoers file

use ycp;
use strict;
use Errno qw(ENOENT);
use Data::Dumper;

my $filename = "/etc/sudoers";

my %data; #= (
#		"Host_Alias" => [ ["# Host Alias Specification","SERVERS", "ns, www, mail"],["","FOO", "www.foo.org"] ],
#		"User_Alias" => [ ["# User Alias Specification", "BAT","foobar"], ["","WWW", "wwwrun"] ],
#		"Cmnd_Alias" => [ ["# Command Alias Specification", "HALT", "/usr/sbin/halt, /usr/sbin/shutdown -h now,"], ["","REBOOT", "/sbin/reboot"] ],
#		"Runas_Alias" => [ ],
#		"Defaults" => [["#Defaults specification","env_reset",""],["","always_set_home",""] ],	
#		'root' => [ ["# User privilege specification", "ALL", "(ALL) ALL"] ],	
#		'%wheel' => [ ["# Same thing without password", "ALL",  "(ALL) NOPASSWD: HALT,REBOOT"] ],	
#	);

sub parse_file {

	if (!open(INFILE, $filename)) {
		return 1 if ($! == ENOENT); #File doesn't exist (yet)
		y2error("Could not open file $filename for reading: %1", $!);
		return 0;
	}

	my $comment = "";
	while (<INFILE>) {
		chomp;
		if (/^\s*$/ || /^#/) {
			$comment .= "$_\n";
			next; 
		}

		my $alias	= "";	
		my @entry = ();
		if (/^(\S+)\s+(\S+)\s*=\s*([^#]*)/) {
			$alias =$1;
			push(@entry,$comment,$2,$3);
		}	
		elsif (/^(\S+)\s+(\S+)/) {
			$alias =$1;
			push(@entry,$comment,$2,"");
		}
	
		push (@{$data{$alias}}, \@entry);

		$comment = "";
	}

	close (INFILE);
	return 1;
}

sub store_key {
	my $key = $_[0];

	if (exists ($data{$key})) {
		my @key_data = @{$data{$key}};
		foreach my $entry (@key_data) {
			my ($comment, $name, $members) = @{$entry};
	
			if($comment){
				print OUTFILE $comment;
			}
			if($name) {
				if($members) {
					print OUTFILE $key,"\t", $name, " = ", $members, "\n";
				}
				else {
					print OUTFILE $key,"\t", $name,"\n";
				}
			}
		}
	}
}

sub store_file {

	open(OUTFILE,">$filename.YaST2.new") 
		or return y2error("Could not open file $filename.YaST2.new for writing: %1", $!), 0;
	
	#Store Host Aliases first and delete old stuff
	store_key("Host_Alias");
	delete($data{"Host_Alias"});
	
	#Store User Aliases next
	store_key("User_Alias");
	delete($data{"User_Alias"});

	#Store Command Aliases next
	store_key("Cmnd_Alias");
	delete($data{"Cmnd_Alias"});

	#Defaults next
	store_key("Defaults");
	delete($data{"Defaults"});

	#Runas Aliases then
	store_key("Runas_Alias");
	delete($data{"Runas_Alias"});

	#Dump the rest
	foreach my $key (keys %data) {
		store_key($key);
		delete($data{$key});
	}

	close(OUTFILE);

	#try syntax checking - non-zero return value of system() means failure
	my $status = system ("visudo", "-c", "-q", "-f", "$filename.YaST2.new"); 
	if ($status != 0){
		return y2error("Syntax error in $filename.YaST2.new"), 0; 
	}

	if (-f $filename) {
		rename $filename, "$filename.YaST2.save" or return y2error("Error creating backup: $!"), 0;
	}
	rename "$filename.YaST2.new", $filename or return y2error("Error moving temp file: $!"), 0;
	
	#Save /etc/sudoers with 0440 access rights - FaTE #300934
	chmod(0440,$filename);
	return 1;
} 

#parse whole file at once, fill in %data structure
parse_file();

#main loop
while ( <STDIN> ) {
	my ($command, $path, $argument) = ycp::ParseCommand ($_);

	if ($command eq "Dir") {
		if ($path eq ".") {
			my @keys = keys %data;
			ycp::Return(\@keys);
		}
		else {
			ycp::Return([]);
		}
	}

	elsif($command eq "Read") {
		#Remove leading '.'
		#$path =~ s/\.//; 
		my @p = ycp::PathComponents(\$path);

		if (exists($data{$p[0]})) {
			ycp::Return(\@{$data{$p[0]} });
		}
		else {
			y2error("Unrecognized key '$p[0]'");
			ycp::Return(undef);
		}
	}

	elsif($command eq "Write") {
		my $result = "true";
		if ($path eq "." && ref($argument) eq "HASH") {
			%data = %{$argument};
			
		}
		elsif ($path eq "." && !defined($argument)) {
			$result = store_file() ? "true" : "false";
		}
		else {
			y2error("Invalid path $path, or argument:", ref($argument));
			$result = "false";
		}

		ycp::Return($result);
	}

	elsif ($command eq "result") {
		exit;
	}

	else {
		y2error("Unknown instruction $command, or argument:", ref ($argument));
		ycp::Return("false");
	}
}

#Debug only !
#print STDERR Dumper(\%data);
