#!/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 @data2 = ();  #= (
#		"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 = "";
        my $line = "";
	while (<INFILE>) {
		chomp;
                $line .= $_;
		#a line is a comment
		if ($line =~ m/^\s*$/ || $line =~ m/^#/) {
			$comment .= "$_\n";
                        $line = "";
			next; 
		}

		#a line is \-terminated multiline rule/alias
		#save it and continue on the next line
                if ($line =~ m/^(.*)\\$/){
                  $line = $1;
                  next;
                }

	        my $alias       = "";

		my @entry2 = ();
		if ($line =~ m/^(\S+)\s+(\S+)\s*=\s*([^#]*)/) {
			$alias =$1;
			push(@entry2, $comment, $alias, $2, $3);
		}	
		elsif ($line =~ m/^(\S+)\s+(\S+)/) {
			$alias =$1;
			push(@entry2, $comment, $alias, $2);
		}
	
		push (@data2, \@entry2);

		$comment = "";
                $line = "";
	}

	close (INFILE);
	return 1;
}

sub store_line {
	my $line = $_[0];
	my ($comment, $type, $name, $members) = @{$line};

	if($comment){
		print OUTFILE $comment;
	}
	if($members) {
		print OUTFILE $type,"\t", $name, " = ", $members, "\n";
	}
	else {
		print OUTFILE $type,"\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;
	
	#Dump the rest
	foreach my $line (@data2) {
		store_line($line);
		#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 "Read") {
		ycp::Return(\@data2);
	}

	elsif($command eq "Write") {
		my $result = "true";
		if ($path eq "." && ref($argument) eq "ARRAY") {
			@data2 = @{$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(\@data2);
