#!/usr/bin/env perl
##
## Samba: Script for 'add/change/delete share command' in the smb.conf
## Copyright (c) 2007 SATOH Fumiyas @ OSS Technology Co., Japan
##                    <http://www.osstech.co.jp/>
##
## Date: 2007-05-17, since 2007-02-21
## License: GNU General Public License version 2
##

use strict;
use warnings;
use IO::File;

umask(0022);
$ENV{'LC_ALL'} = 'C';
$ENV{'LANG'} = 'C';

## Run a command without /bin/sh and die on error
sub run {
    my $cmd = shift(@_);
    if (my $status = system {$cmd} $cmd, @_) {
	if ($status == -1) {
	    die "$0: exec or wait failed: $cmd: $!\n";
	}
	else {
	    die "$0: command failed: $cmd: status ", $status>>8, "\n";
	}
    }
}

## Run a command command and warn on error
sub runw {
    eval {run(@_)};
    warn "$@" if ($@);
}

## Run a command and ignore error
sub runi {
    eval {run(@_)};
}

my $cmd_name = $ENV{'SMBSHAREADM_OP'} || $0;

my $op;
if ($cmd_name =~ /(add|change|chg|ch|modify|mod)$/) {
    $op = 'add';
}
elsif ($cmd_name =~ /(delete|del)$/) {
    $op = 'delete';
}
else {
    die "$0: invalid command name\n";
}

if (@ARGV != 5) {
    print "Usage: $0 SMB.CONF NAME PATH COMMENT MAXCONNECTION\n";
    exit(0);
}

my (
    $smbconf_file,
    $share_name,
    $share_path,
    $share_comment,
    $share_maxconn,
) = @ARGV;

$share_name =~ s/^\s+//;
$share_name =~ s/\s+$//;
if ($share_name =~ /^(global|homes|printers)$/i) {
    die "$0: cannot treat the special share name: $share_name\n";
}
if ($share_name =~ /[\/\\\[\]]/) {
    die "$0: invalid character in the share name: $share_name\n";
}

my $smbconf_old = "$smbconf_file.old.$$.tmp";
my $smbconf_new = "$smbconf_file.new.$$.tmp";

eval {
    $SIG{'INT'} = $SIG{'TERM'} = $SIG{'PIPE'} = sub {die "$0: interrupted by signal\n"};

    run('cp', $smbconf_file, $smbconf_old);

    my $old_fh = IO::File->new($smbconf_old, 'r')
	|| die "$0: cannot open file: $smbconf_old: $!\n";
    my $new_fh = IO::File->new($smbconf_new, 'w')
	|| die "$0: cannot open file: $smbconf_new: $!\n";

    ## Passthrough while reaching to '[$share_name]' or EOF
    while (defined(my $line = $old_fh->getline)) {
	if ($line =~ /^\s*\[\s*\Q$share_name\E\s*\].*$/io) {
	    last;
	}
	$new_fh->print($line);
    }

    ## Create the [$share_name] if $op is 'add' (or 'modify')
    if ($op eq 'add') {
	$new_fh->print("\n") if ($old_fh->eof);
	$new_fh->print("[$share_name]\n");
	$new_fh->print("\tpath = $share_path\n");
	$new_fh->print("\tcomment = $share_comment\n");
	$new_fh->print("\tmax connections = $share_maxconn\n");
	$new_fh->print("\twriteable = yes\n");
    }

    ## Process the remains of part in the [$share_name]
    while (defined(my $line = $old_fh->getline)) {
	## Next share?
	if ($line =~ /^\s*\[/) {
	    $new_fh->print($line);
	    last;
	}
	## Ignore parameters if $op is 'delete'
	if ($op eq 'delete') {
	    next;
	}
	## Ignore old overrided parameters (if any)
	if ($line =~ /^([^=]+)=/) {
	    my $pname = lc($1);	## Case insensitive
	    $pname =~ s/\s+//g; ## Space insensitive
	    if ($pname =~ /^(path|comment|maxconnections)$/) {
		next;
	    }
	}

	## Passthrough
	$new_fh->print($line);
    }

    ## Passthrough the remains
    $new_fh->print($old_fh->getlines);

    if (defined($ENV{'SMBSHAREADM_TEST'})) {
	## Test mode
	run('cat', $smbconf_new);
	runi('diff', '-u', $smbconf_file, $smbconf_new);
    }
    else {
	## Update config
	run('mv', $smbconf_new, $smbconf_file);
    }
};

## Cleanup before exiting
unlink($smbconf_old, $smbconf_new);

if ($@) {
    die("$@");
}

exit(0);

